Skip to content

Commit

Permalink
Markdown in Survey surveyjs/service#2319 (#5643)
Browse files Browse the repository at this point in the history
* Markdown in Survey surveyjs/service#2319

* Markdown fixes surveyjs/service#2319

* markdown - use native code on focus

* fix old f-test
  • Loading branch information
novikov82 authored Jul 9, 2024
1 parent 9f50fcc commit 2d758bd
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
</div>
<span class="svc-string-editor__input">
<span role="textbox" *ngIf="!locString.hasHtml" class="sv-string-editor" spellcheck="false"
(focus)="onFocus($event)" (paste)="onPaste($event)" (blur)="onBlur($event)" (input)="baseModel.onInput($event)" (keydown)="baseModel.onKeyDown($event)" (keyup)="baseModel.onKeyUp($event)" (compositionstart)="baseModel.onCompositionStart($event)" (compositionend)="baseModel.onCompositionEnd($event)" (mouseup)="baseModel.onMouseUp($event)" (click)="edit($event)" [textContent]="editValue" [attr.aria-placeholder]="placeholder" [attr.contenteditable]="contentEditable" #container></span>
(focus)="onFocus($event)" (paste)="onPaste($event)" (blur)="onBlur($event)" (input)="baseModel.onInput($event)" (keydown)="baseModel.onKeyDown($event)" (keyup)="baseModel.onKeyUp($event)" (compositionstart)="baseModel.onCompositionStart($event)" (compositionend)="baseModel.onCompositionEnd($event)" (mouseup)="baseModel.onMouseUp($event)" (click)="edit($event)" [textContent]="locString.renderedHtml" [attr.aria-placeholder]="placeholder" [attr.contenteditable]="contentEditable" #container></span>
<span role="textbox" *ngIf="locString.hasHtml" class="sv-string-editor sv-string-editor--html" spellcheck="false"
(focus)="onFocus($event)" (blur)="onBlur($event)" (keydown)="baseModel.onKeyDown($event)" (keyup)="baseModel.onKeyUp($event)" (compositionstart)="baseModel.onCompositionStart($event)" (compositionend)="baseModel.onCompositionEnd($event)" (mouseup)="baseModel.onMouseUp($event)"
(click)="edit($event)" [attr.aria-placeholder]="placeholder" [attr.contenteditable]="contentEditable" [innerHtml]="editValue | safeHtml" #container></span>
(click)="edit($event)" [attr.aria-placeholder]="placeholder" [attr.contenteditable]="contentEditable" [innerHtml]="locString.renderedHtml | safeHtml" #container></span>
<sv-ng-character-counter *ngIf="showCharacterCounter" [counter]="characterCounter" [remainingCharacterCounter]="getCharacterCounterClass"></sv-ng-character-counter>
</span>
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ export class StringEditorComponent extends CreatorModelComponent<StringEditorVie
public get errorText(): string {
return this.baseModel.errorText;
}
public get editValue(): string {
return this.baseModel.focused && this.baseModel.editAsText && this.locString.text || this.locString.renderedHtml;
}
onChangeHandler = (): void => {
this.detectChanges();
}
Expand Down
8 changes: 7 additions & 1 deletion packages/survey-creator-core/src/components/string-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ export class StringEditorViewModelBase extends Base {
if(this.creator) {
this.creator.selectFromStringEditor = true;
}

if (this.locString.hasHtml && this.editAsText) {
event.target.innerText = event.target.textContent = this.locString.text;
}

event.target.parentElement.click();
event.target.spellcheck = true;
event.target.setAttribute("tabindex", -1);
Expand Down Expand Up @@ -399,6 +404,7 @@ export class StringEditorViewModelBase extends Base {
this.focused = false;
window?.getSelection().removeAllRanges();
}

private getClearedText(target: HTMLElement): string {
const html = target.innerHTML;
const text = target.innerText;
Expand All @@ -418,7 +424,7 @@ export class StringEditorViewModelBase extends Base {
if (mdText) {
clearedText = mdText;
} else {
let txt = this.locString.hasHtml ? html : text;
let txt = this.locString.hasHtml && !this.editAsText ? html : text;
if (!this.locString.allowLineBreaks) txt = clearNewLines(txt);
clearedText = txt;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/survey-creator-knockout/src/string-editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
<!-- ko ifnot: koHasHtml -->
<!-- ko template: {afterRender: afterRender} -->
<span role="textbox" class="sv-string-editor" spellcheck="false"
data-bind="text: editValue, event: { focus: onFocus, paste: onPaste, blur: onBlur, input: onInput, keydown: onKeyDown, keyup: onKeyUp, mouseup: onMouseUp, compositionstart: onCompositionStart, compositionend: onCompositionEnd }, click: edit, attr: { 'aria-placeholder': placeholder, 'aria-label': this.placeholder || 'content editable', contenteditable: contentEditable }"></span>
data-bind="text: koRenderedHtml, event: { focus: onFocus, paste: onPaste, blur: onBlur, input: onInput, keydown: onKeyDown, keyup: onKeyUp, mouseup: onMouseUp, compositionstart: onCompositionStart, compositionend: onCompositionEnd }, click: edit, attr: { 'aria-placeholder': placeholder, 'aria-label': this.placeholder || 'content editable', contenteditable: contentEditable }"></span>
<!-- /ko -->
<!-- /ko -->
<!-- ko if: koHasHtml -->
<span role="textbox" class="sv-string-editor sv-string-editor--html" spellcheck="false"
data-bind="html: editValue, event: { focus: onFocus, blur: onBlur, keydown: onKeyDown, keyup: onKeyUp, mouseup: onMouseUp, compositionstart: onCompositionStart, compositionend: onCompositionEnd }, click: edit, attr: { 'aria-placeholder': placeholder, 'aria-label': this.placeholder || 'content editable', contenteditable: contentEditable }"></span>
data-bind="html: koRenderedHtml, event: { focus: onFocus, blur: onBlur, keydown: onKeyDown, keyup: onKeyUp, mouseup: onMouseUp, compositionstart: onCompositionStart, compositionend: onCompositionEnd }, click: edit, attr: { 'aria-placeholder': placeholder, 'aria-label': this.placeholder || 'content editable', contenteditable: contentEditable }"></span>
<!-- /ko -->
<!-- ko if: showCharacterCounter -->
<!-- ko component: { name: 'sv-character-counter', params: { counter: characterCounter, remainingCharacterCounter: getCharacterCounterClass } } -->
Expand Down
4 changes: 2 additions & 2 deletions packages/survey-creator-knockout/src/string-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export class StringEditorViewModel {
public get koHasHtml(): boolean {
return this.locString.koHasHtml();
}
public get editValue(): string {
return this.baseModel.focused && this.baseModel.editAsText && this.locString.text || this.locString.koRenderedHtml();
public get koRenderedHtml(): string {
return this.locString.koRenderedHtml();
}
public get className(): string {
return this.baseModel.className(this.locString.koRenderedHtml());
Expand Down
2 changes: 1 addition & 1 deletion packages/survey-creator-react/src/StringEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class SurveyLocStringEditor extends CreatorModelElement<any, any> {
}
let control = null;
if (this.locString.hasHtml) {
const htmlValue = { __html: this.baseModel.focused && this.baseModel.editAsText && this.locString.text || this.locString.renderedHtml };
const htmlValue = { __html: this.locString.renderedHtml };
control = (
<span
role="textbox"
Expand Down
12 changes: 3 additions & 9 deletions packages/survey-creator-vue/src/StringEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
@compositionend="baseModel?.onCompositionEnd"
@mouseup="baseModel?.onMouseUp"
@click="edit"
:textContent="editValue"
:textContent="renderedHtml"
:aria-placeholder="placeholder"
:contenteditable="contentEditable"
ref="root"
Expand All @@ -45,7 +45,7 @@
@click="edit"
:aria-placeholder="placeholder"
:contenteditable="contentEditable"
v-html="editValue"
v-html="renderedHtml"
ref="root"
></span>
<sv-character-counter
Expand Down Expand Up @@ -107,13 +107,7 @@ const baseModel = useCreatorModel(
}
);
const editValue = computed<string>(
() =>
(baseModel.value?.focused &&
baseModel.value.editAsText &&
locString.value.text) ||
(renderedHtml.value as string)
);
const errorText = computed(() => baseModel.value?.errorText);
const className = computed(() => {
return baseModel.value?.className(locString.value.renderedHtml);
Expand Down
39 changes: 34 additions & 5 deletions testCafe/designer/string-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,17 +426,17 @@ test("Check markdown events with HTML", async (t) => {
await t
.expect(getValue()).eql("*a<b>b</b>c*")
.click(Selector(".sv-string-editor").withText("$abc$"))
.expect(Selector(".sv-string-editor").withText("*abc*").visible).ok()
.click(Selector(".sv-string-editor").withText("*abc*"))
.expect(Selector(".sv-string-editor").withText("*a<b>b</b>c*").visible).ok()
.click(Selector(".sv-string-editor").withText("*a<b>b</b>c*"))
.wait(100)
.pressKey("esc")
.wait(100)
.expect(Selector(".sv-string-editor").withText("$abc$").visible).ok()
.expect(getValue()).eql("*a<b>b</b>c*")
.click(Selector(".sv-string-editor").withText("$abc$"))
.expect(Selector(".sv-string-editor").withText("*abc*").visible).ok()
.click(Selector(".sv-string-editor").withText("*abc*"))
.typeText(Selector(".sv-string-editor").withText("*abc*"), "d", { caretPos: 0 })
.expect(Selector(".sv-string-editor").withText("*a<b>b</b>c*").visible).ok()
.click(Selector(".sv-string-editor").withText("*a<b>b</b>c*"))
.typeText(Selector(".sv-string-editor").withText("*a<b>b</b>c*"), "d", { caretPos: 0 })
.pressKey("enter")
.expect(Selector(".sv-string-editor").withText("d$abc$").visible).ok()
.expect(getValue()).eql("d*a<b>b</b>c*");
Expand Down Expand Up @@ -925,6 +925,35 @@ test("Check string editor with html", async (t) => {
await t.expect(htmlMarkupSelector.getStyleProperty("color")).eql("rgb(255, 0, 255)");
});

test("Check string editor with html security", async (t) => {
await explicitErrorHandler();
await ClientFunction(() => {
(<any>window).creator.onDesignerSurveyCreated.add(function (editor, options) {
options.survey.onTextMarkdown.add((_, opt) => {
opt.html = opt.text.replaceAll("<", "[").replaceAll(">", "]");
});
});
})();

await setJSON({
"elements": [
{
"type": "text",
title: "<p>Test</p>",
"name": "q1",
}]
});

const htmlMarkupSelector = Selector(".sv-string-editor--html").withText("Test");
await t.expect(htmlMarkupSelector.visible).ok();

await t.expect(ClientFunction(() => document.querySelector(".sd-question[data-name=q1] .sv-string-editor").innerHTML)()).eql("[p]Test[/p]");
await t.click(htmlMarkupSelector);
await t.expect(ClientFunction(() => document.querySelector(".sd-question[data-name=q1] .sv-string-editor").innerHTML)()).eql("&lt;p&gt;Test&lt;/p&gt;");
await t.pressKey("enter");
await t.expect(ClientFunction(() => document.querySelector(".sd-question[data-name=q1] .sv-string-editor").innerHTML)()).eql("[p]Test[/p]");
});

test("Check string editor focus does not throw error: #4459", async (t) => {
await explicitErrorHandler();
await setJSON({
Expand Down

0 comments on commit 2d758bd

Please sign in to comment.