testomatio-editor-blocks 0.2.0 → 0.2.2
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/editor/blocks/snippet.js +8 -1
- package/package/editor/blocks/step.js +18 -2
- package/package/editor/blocks/stepField.d.ts +2 -1
- package/package/editor/blocks/stepField.js +2 -1
- package/package/index.d.ts +3 -0
- package/package/index.js +3 -0
- package/package/styles.css +27 -0
- package/package.json +1 -1
- package/src/editor/blocks/snippet.tsx +15 -0
- package/src/editor/blocks/step.tsx +28 -0
- package/src/editor/blocks/stepField.tsx +3 -0
- package/src/editor/styles.css +27 -0
- package/src/index.ts +3 -0
|
@@ -25,6 +25,7 @@ export const snippetBlock = createReactBlockSpec({
|
|
|
25
25
|
const snippetTitle = block.props.snippetTitle || "";
|
|
26
26
|
const snippetData = block.props.snippetData || "";
|
|
27
27
|
const snippetSuggestions = useSnippetAutocomplete();
|
|
28
|
+
const hasSnippets = snippetSuggestions.length > 0;
|
|
28
29
|
const handleSnippetChange = useCallback((nextTitle) => {
|
|
29
30
|
if (nextTitle === snippetTitle) {
|
|
30
31
|
return;
|
|
@@ -60,6 +61,12 @@ export const snippetBlock = createReactBlockSpec({
|
|
|
60
61
|
},
|
|
61
62
|
});
|
|
62
63
|
}, [block.id, editor]);
|
|
63
|
-
|
|
64
|
+
const handleFieldFocus = useCallback(() => {
|
|
65
|
+
editor.setSelection(block.id, block.id);
|
|
66
|
+
}, [editor, block.id]);
|
|
67
|
+
if (!hasSnippets) {
|
|
68
|
+
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." }) }));
|
|
69
|
+
}
|
|
70
|
+
return (_jsxs("div", { className: "bn-teststep bn-snippet", "data-block-id": block.id, children: [_jsx(StepField, { label: "Snippet Title", value: snippetTitle, placeholder: "Describe the reusable action", onChange: handleSnippetChange, autoFocus: snippetTitle.length === 0, enableAutocomplete: true, suggestionFilter: (suggestion) => suggestion.isSnippet === true, suggestionsOverride: snippetSuggestions, onSuggestionSelect: handleSnippetSelect, fieldName: "snippet-title", showSuggestionsOnFocus: true, enableImageUpload: false, onFieldFocus: handleFieldFocus }), _jsx(StepField, { label: "Snippet Data", value: snippetData, placeholder: "Add optional data or assets for the snippet", onChange: handleSnippetDataChange, multiline: true, fieldName: "snippet-data", enableImageUpload: true, onFieldFocus: handleFieldFocus })] }));
|
|
64
71
|
},
|
|
65
72
|
});
|
|
@@ -72,7 +72,23 @@ export const stepBlock = createReactBlockSpec({
|
|
|
72
72
|
},
|
|
73
73
|
});
|
|
74
74
|
}, [editor, block.id, expectedResult]);
|
|
75
|
-
|
|
75
|
+
const handleInsertNextStep = useCallback(() => {
|
|
76
|
+
editor.insertBlocks([
|
|
77
|
+
{
|
|
78
|
+
type: "testStep",
|
|
79
|
+
props: {
|
|
80
|
+
stepTitle: "",
|
|
81
|
+
stepData: "",
|
|
82
|
+
expectedResult: "",
|
|
83
|
+
},
|
|
84
|
+
children: [],
|
|
85
|
+
},
|
|
86
|
+
], block.id, "after");
|
|
87
|
+
}, [editor, block.id]);
|
|
88
|
+
const handleFieldFocus = useCallback(() => {
|
|
89
|
+
editor.setSelection(block.id, block.id);
|
|
90
|
+
}, [editor, block.id]);
|
|
91
|
+
return (_jsxs("div", { className: "bn-teststep", "data-block-id": block.id, children: [_jsx(StepField, { label: "Step Title", value: stepTitle, placeholder: "Describe the action to perform", onChange: handleStepTitleChange, autoFocus: stepTitle.length === 0, enableAutocomplete: true, fieldName: "title", suggestionFilter: (suggestion) => suggestion.isSnippet !== true, onFieldFocus: handleFieldFocus, rightAction: !isDataVisible ? (_jsx("button", { type: "button", className: "bn-teststep__toggle", onClick: handleShowDataField, "aria-expanded": "false", tabIndex: -1, children: "+ Step Data" })) : null, enableImageUpload: false, showFormattingButtons: true, onImageFile: async (file) => {
|
|
76
92
|
if (!uploadImage) {
|
|
77
93
|
return;
|
|
78
94
|
}
|
|
@@ -92,6 +108,6 @@ export const stepBlock = createReactBlockSpec({
|
|
|
92
108
|
catch (error) {
|
|
93
109
|
console.error("Failed to upload image to Step Data", error);
|
|
94
110
|
}
|
|
95
|
-
} }), isDataVisible && (_jsx(StepField, { label: "Step Data", value: stepData, placeholder: "Provide additional data about the step", onChange: handleStepDataChange, autoFocus: shouldFocusDataField, multiline: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true })), showExpectedField && (_jsx(StepField, { label: "Expected Result", value: expectedResult, placeholder: "What should happen?", onChange: handleExpectedChange, multiline: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true }))] }));
|
|
111
|
+
} }), isDataVisible && (_jsx(StepField, { label: "Step Data", value: stepData, placeholder: "Provide additional data about the step", onChange: handleStepDataChange, autoFocus: shouldFocusDataField, multiline: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })), showExpectedField && (_jsx(StepField, { label: "Expected Result", value: expectedResult, placeholder: "What should happen?", onChange: handleExpectedChange, multiline: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })), _jsx("button", { type: "button", className: "bn-step-add", onClick: handleInsertNextStep, children: "+ Step" })] }));
|
|
96
112
|
},
|
|
97
113
|
});
|
|
@@ -21,6 +21,7 @@ type StepFieldProps = {
|
|
|
21
21
|
rightAction?: ReactNode;
|
|
22
22
|
showFormattingButtons?: boolean;
|
|
23
23
|
showImageButton?: boolean;
|
|
24
|
+
onFieldFocus?: () => void;
|
|
24
25
|
};
|
|
25
|
-
export declare function StepField({ label, value, placeholder, onChange, autoFocus, multiline, enableAutocomplete, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly, showSuggestionsOnFocus, enableImageUpload, onImageFile, rightAction, showFormattingButtons, showImageButton, }: StepFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function StepField({ label, value, placeholder, onChange, autoFocus, multiline, enableAutocomplete, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly, showSuggestionsOnFocus, enableImageUpload, onImageFile, rightAction, showFormattingButtons, showImageButton, onFieldFocus, }: StepFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
26
27
|
export {};
|
|
@@ -3,7 +3,7 @@ import { useStepAutocomplete } from "../stepAutocomplete";
|
|
|
3
3
|
import { useStepImageUpload } from "../stepImageUpload";
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { escapeHtml, escapeMarkdownText, htmlToMarkdown, markdownToHtml, normalizePlainText, } from "./markdown";
|
|
6
|
-
export function StepField({ label, value, placeholder, onChange, autoFocus, multiline = false, enableAutocomplete = false, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly = false, showSuggestionsOnFocus = false, enableImageUpload = false, onImageFile, rightAction, showFormattingButtons = false, showImageButton = false, }) {
|
|
6
|
+
export function StepField({ label, value, placeholder, onChange, autoFocus, multiline = false, enableAutocomplete = false, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly = false, showSuggestionsOnFocus = false, enableImageUpload = false, onImageFile, rightAction, showFormattingButtons = false, showImageButton = false, onFieldFocus, }) {
|
|
7
7
|
const editorRef = useRef(null);
|
|
8
8
|
const [isFocused, setIsFocused] = useState(false);
|
|
9
9
|
const autoFocusRef = useRef(false);
|
|
@@ -240,6 +240,7 @@ export function StepField({ label, value, placeholder, onChange, autoFocus, mult
|
|
|
240
240
|
if (showSuggestionsOnFocus && enableAutocomplete) {
|
|
241
241
|
setShowAllSuggestions(true);
|
|
242
242
|
}
|
|
243
|
+
onFieldFocus === null || onFieldFocus === void 0 ? void 0 : onFieldFocus();
|
|
243
244
|
setPlainTextValue((_b = (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.innerText) !== null && _b !== void 0 ? _b : "");
|
|
244
245
|
}, onBlur: () => {
|
|
245
246
|
setIsFocused(false);
|
package/package/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { customSchema, type CustomSchema, type CustomBlock, type CustomEditor, } from "./editor/customSchema";
|
|
2
|
+
export { stepBlock } from "./editor/blocks/step";
|
|
3
|
+
export { snippetBlock } from "./editor/blocks/snippet";
|
|
4
|
+
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
|
|
2
5
|
export { blocksToMarkdown, markdownToBlocks, type CustomEditorBlock, type CustomPartialBlock, } from "./editor/customMarkdownConverter";
|
|
3
6
|
export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, type StepSuggestion, type StepJsonApiDocument, type StepJsonApiResource, } from "./editor/stepAutocomplete";
|
|
4
7
|
export { useStepImageUpload, setImageUploadHandler, type StepImageUploadHandler, } from "./editor/stepImageUpload";
|
package/package/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { customSchema, } from "./editor/customSchema";
|
|
2
|
+
export { stepBlock } from "./editor/blocks/step";
|
|
3
|
+
export { snippetBlock } from "./editor/blocks/snippet";
|
|
4
|
+
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
|
|
2
5
|
export { blocksToMarkdown, markdownToBlocks, } from "./editor/customMarkdownConverter";
|
|
3
6
|
export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, } from "./editor/stepAutocomplete";
|
|
4
7
|
export { useStepImageUpload, setImageUploadHandler, } from "./editor/stepImageUpload";
|
package/package/styles.css
CHANGED
|
@@ -169,6 +169,33 @@
|
|
|
169
169
|
color: rgba(15, 118, 110, 0.65);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
.bn-snippet__empty {
|
|
173
|
+
margin: 0;
|
|
174
|
+
font-size: 0.9rem;
|
|
175
|
+
color: rgba(15, 23, 42, 0.65);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.bn-step-add {
|
|
179
|
+
align-self: flex-start;
|
|
180
|
+
margin-top: 0.25rem;
|
|
181
|
+
border: none;
|
|
182
|
+
background: rgba(37, 99, 235, 0.12);
|
|
183
|
+
color: #1d4ed8;
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
font-size: 0.85rem;
|
|
186
|
+
padding: 0.35rem 0.65rem;
|
|
187
|
+
border-radius: 0.5rem;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
transition:
|
|
190
|
+
background-color 120ms ease,
|
|
191
|
+
box-shadow 120ms ease;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.bn-step-add:hover {
|
|
195
|
+
background: rgba(37, 99, 235, 0.2);
|
|
196
|
+
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.18);
|
|
197
|
+
}
|
|
198
|
+
|
|
172
199
|
.bn-teststep__toggle {
|
|
173
200
|
align-self: flex-start;
|
|
174
201
|
padding: 0.35rem 0.6rem;
|
package/package.json
CHANGED
|
@@ -28,6 +28,7 @@ export const snippetBlock = createReactBlockSpec(
|
|
|
28
28
|
const snippetTitle = (block.props.snippetTitle as string) || "";
|
|
29
29
|
const snippetData = (block.props.snippetData as string) || "";
|
|
30
30
|
const snippetSuggestions = useSnippetAutocomplete();
|
|
31
|
+
const hasSnippets = snippetSuggestions.length > 0;
|
|
31
32
|
|
|
32
33
|
const handleSnippetChange = useCallback(
|
|
33
34
|
(nextTitle: string) => {
|
|
@@ -77,6 +78,18 @@ export const snippetBlock = createReactBlockSpec(
|
|
|
77
78
|
[block.id, editor],
|
|
78
79
|
);
|
|
79
80
|
|
|
81
|
+
const handleFieldFocus = useCallback(() => {
|
|
82
|
+
editor.setSelection(block.id, block.id);
|
|
83
|
+
}, [editor, block.id]);
|
|
84
|
+
|
|
85
|
+
if (!hasSnippets) {
|
|
86
|
+
return (
|
|
87
|
+
<div className="bn-teststep bn-snippet" data-block-id={block.id}>
|
|
88
|
+
<p className="bn-snippet__empty">No snippets in this project.</p>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
80
93
|
return (
|
|
81
94
|
<div className="bn-teststep bn-snippet" data-block-id={block.id}>
|
|
82
95
|
<StepField
|
|
@@ -92,6 +105,7 @@ export const snippetBlock = createReactBlockSpec(
|
|
|
92
105
|
fieldName="snippet-title"
|
|
93
106
|
showSuggestionsOnFocus
|
|
94
107
|
enableImageUpload={false}
|
|
108
|
+
onFieldFocus={handleFieldFocus}
|
|
95
109
|
/>
|
|
96
110
|
<StepField
|
|
97
111
|
label="Snippet Data"
|
|
@@ -101,6 +115,7 @@ export const snippetBlock = createReactBlockSpec(
|
|
|
101
115
|
multiline
|
|
102
116
|
fieldName="snippet-data"
|
|
103
117
|
enableImageUpload
|
|
118
|
+
onFieldFocus={handleFieldFocus}
|
|
104
119
|
/>
|
|
105
120
|
</div>
|
|
106
121
|
);
|
|
@@ -95,6 +95,28 @@ export const stepBlock = createReactBlockSpec(
|
|
|
95
95
|
[editor, block.id, expectedResult],
|
|
96
96
|
);
|
|
97
97
|
|
|
98
|
+
const handleInsertNextStep = useCallback(() => {
|
|
99
|
+
editor.insertBlocks(
|
|
100
|
+
[
|
|
101
|
+
{
|
|
102
|
+
type: "testStep",
|
|
103
|
+
props: {
|
|
104
|
+
stepTitle: "",
|
|
105
|
+
stepData: "",
|
|
106
|
+
expectedResult: "",
|
|
107
|
+
},
|
|
108
|
+
children: [],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
block.id,
|
|
112
|
+
"after",
|
|
113
|
+
);
|
|
114
|
+
}, [editor, block.id]);
|
|
115
|
+
|
|
116
|
+
const handleFieldFocus = useCallback(() => {
|
|
117
|
+
editor.setSelection(block.id, block.id);
|
|
118
|
+
}, [editor, block.id]);
|
|
119
|
+
|
|
98
120
|
return (
|
|
99
121
|
<div className="bn-teststep" data-block-id={block.id}>
|
|
100
122
|
<StepField
|
|
@@ -106,6 +128,7 @@ export const stepBlock = createReactBlockSpec(
|
|
|
106
128
|
enableAutocomplete
|
|
107
129
|
fieldName="title"
|
|
108
130
|
suggestionFilter={(suggestion) => (suggestion as StepSuggestion).isSnippet !== true}
|
|
131
|
+
onFieldFocus={handleFieldFocus}
|
|
109
132
|
rightAction={
|
|
110
133
|
!isDataVisible ? (
|
|
111
134
|
<button
|
|
@@ -154,6 +177,7 @@ export const stepBlock = createReactBlockSpec(
|
|
|
154
177
|
enableImageUpload
|
|
155
178
|
showFormattingButtons
|
|
156
179
|
showImageButton
|
|
180
|
+
onFieldFocus={handleFieldFocus}
|
|
157
181
|
/>
|
|
158
182
|
)}
|
|
159
183
|
{showExpectedField && (
|
|
@@ -166,8 +190,12 @@ export const stepBlock = createReactBlockSpec(
|
|
|
166
190
|
enableImageUpload
|
|
167
191
|
showFormattingButtons
|
|
168
192
|
showImageButton
|
|
193
|
+
onFieldFocus={handleFieldFocus}
|
|
169
194
|
/>
|
|
170
195
|
)}
|
|
196
|
+
<button type="button" className="bn-step-add" onClick={handleInsertNextStep}>
|
|
197
|
+
+ Step
|
|
198
|
+
</button>
|
|
171
199
|
</div>
|
|
172
200
|
);
|
|
173
201
|
},
|
|
@@ -32,6 +32,7 @@ type StepFieldProps = {
|
|
|
32
32
|
rightAction?: ReactNode;
|
|
33
33
|
showFormattingButtons?: boolean;
|
|
34
34
|
showImageButton?: boolean;
|
|
35
|
+
onFieldFocus?: () => void;
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
export function StepField({
|
|
@@ -53,6 +54,7 @@ export function StepField({
|
|
|
53
54
|
rightAction,
|
|
54
55
|
showFormattingButtons = false,
|
|
55
56
|
showImageButton = false,
|
|
57
|
+
onFieldFocus,
|
|
56
58
|
}: StepFieldProps) {
|
|
57
59
|
const editorRef = useRef<HTMLDivElement>(null);
|
|
58
60
|
const [isFocused, setIsFocused] = useState(false);
|
|
@@ -376,6 +378,7 @@ export function StepField({
|
|
|
376
378
|
if (showSuggestionsOnFocus && enableAutocomplete) {
|
|
377
379
|
setShowAllSuggestions(true);
|
|
378
380
|
}
|
|
381
|
+
onFieldFocus?.();
|
|
379
382
|
setPlainTextValue(editorRef.current?.innerText ?? "");
|
|
380
383
|
}}
|
|
381
384
|
onBlur={() => {
|
package/src/editor/styles.css
CHANGED
|
@@ -169,6 +169,33 @@
|
|
|
169
169
|
color: rgba(15, 118, 110, 0.65);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
.bn-snippet__empty {
|
|
173
|
+
margin: 0;
|
|
174
|
+
font-size: 0.9rem;
|
|
175
|
+
color: rgba(15, 23, 42, 0.65);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.bn-step-add {
|
|
179
|
+
align-self: flex-start;
|
|
180
|
+
margin-top: 0.25rem;
|
|
181
|
+
border: none;
|
|
182
|
+
background: rgba(37, 99, 235, 0.12);
|
|
183
|
+
color: #1d4ed8;
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
font-size: 0.85rem;
|
|
186
|
+
padding: 0.35rem 0.65rem;
|
|
187
|
+
border-radius: 0.5rem;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
transition:
|
|
190
|
+
background-color 120ms ease,
|
|
191
|
+
box-shadow 120ms ease;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.bn-step-add:hover {
|
|
195
|
+
background: rgba(37, 99, 235, 0.2);
|
|
196
|
+
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.18);
|
|
197
|
+
}
|
|
198
|
+
|
|
172
199
|
.bn-teststep__toggle {
|
|
173
200
|
align-self: flex-start;
|
|
174
201
|
padding: 0.35rem 0.6rem;
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,9 @@ export {
|
|
|
4
4
|
type CustomBlock,
|
|
5
5
|
type CustomEditor,
|
|
6
6
|
} from "./editor/customSchema";
|
|
7
|
+
export { stepBlock } from "./editor/blocks/step";
|
|
8
|
+
export { snippetBlock } from "./editor/blocks/snippet";
|
|
9
|
+
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
|
|
7
10
|
|
|
8
11
|
export {
|
|
9
12
|
blocksToMarkdown,
|