Skip to content

Commit

Permalink
Markdown and displayed value in dropdown question (#4882)
Browse files Browse the repository at this point in the history
* work for #4765

* work for #4765 : fix f-tests

* work for #4765 : update etalons

* work for #4765 : add visual tests

* work for #4765 : add markup test

* work for #4765 : fix dropdown in default & modern themes

* work for #4765 : fix f-tests

Co-authored-by: OlgaLarina <olga.larina.dev@gmail.com>
  • Loading branch information
OlgaLarina and OlgaLarina committed Sep 22, 2022
1 parent bad58e1 commit 6e7e13f
Show file tree
Hide file tree
Showing 29 changed files with 173 additions and 32 deletions.
1 change: 1 addition & 0 deletions examples/angular-ui/src/index.html
Expand Up @@ -2,6 +2,7 @@
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.6.4/showdown.min.js"></script>
<meta charset="utf-8">
<title>SurveyJS Angular</title>
<base href="/">
Expand Down
1 change: 1 addition & 0 deletions examples_test/defaultV2/knockout.html
Expand Up @@ -4,6 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.6.4/showdown.min.js"></script>
<link rel="stylesheet" href="../../build/survey-knockout/defaultV2.min.css" />
<script src="../../build/survey-knockout/survey.ko.min.js"></script>
</head>
Expand Down
1 change: 1 addition & 0 deletions examples_test/defaultV2/react.html
Expand Up @@ -3,6 +3,7 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.6.4/showdown.min.js"></script>
<script src="https://unpkg.com/react@16.5.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.5.0/umd/react-dom.production.min.js"></script>
<link rel="stylesheet" href="../../build/survey-react/defaultV2.min.css" />
Expand Down
1 change: 1 addition & 0 deletions examples_test/defaultV2/vue.html
Expand Up @@ -2,6 +2,7 @@
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.6.4/showdown.min.js"></script>
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
<link rel="stylesheet" href="../../build/survey-vue/defaultV2.min.css" />
<script src="../../build/survey-vue/survey.vue.min.js"></script>
Expand Down
Expand Up @@ -5,6 +5,7 @@
[attr.aria-label]="model.ariaLabel" [attr.aria-invalid]="model.ariaInvalid"
[attr.aria-describedby]="model.ariaDescribedBy" [attr.role]="model.ariaRole">
<div [class]="model.cssClasses.controlValue">
<sv-ng-string *ngIf="model.selectedItemLocText && !model.inputHasValue" [model]="model.selectedItemLocText"></sv-ng-string>
<input #inputElement type="text" autocomplete="off" [(ngModel)]="dropdownModel.filterString" [class]="model.cssClasses.filterStringInput"
[attr.id]="model.getInputId()"
[attr.tabindex]="dropdownModel.inputReadOnly ? null : -1"
Expand Down
5 changes: 5 additions & 0 deletions src/defaultV2-theme/blocks/sd-dropdown.scss
Expand Up @@ -31,6 +31,8 @@
font-family: $font-family;
font-size: calcSize(2);
color: $foreground;

position: relative;
}

.sd-dropdown_clean-button {
Expand Down Expand Up @@ -67,6 +69,9 @@
display: inline-block;
line-height: calcSize(3);
appearance: none;

position: absolute;
left: 0;
}
.sd-dropdown--empty:not(.sd-input--disabled) .sd-dropdown__filter-string-input::placeholder {
color: $foreground-light;
Expand Down
1 change: 1 addition & 0 deletions src/defaultV2-theme/blocks/sd-tagbox.scss
Expand Up @@ -65,6 +65,7 @@
width: auto;
display: flex;
flex-grow: 1;
position: initial;
}
.sd-tagbox__placeholder {
position: absolute;
Expand Down
5 changes: 5 additions & 0 deletions src/dropdownListModel.ts
Expand Up @@ -43,6 +43,7 @@ export class DropdownListModel extends Base {
if(!this.listModel.focusedItem || !this.listModel.isItemVisible(this.listModel.focusedItem)) {
this.listModel.focusFirstVisibleItem();
}
this.setInputHasValue(!!newValue);
}

protected onHidePopup(): void {
Expand Down Expand Up @@ -79,6 +80,9 @@ export class DropdownListModel extends Base {
this.filterString = undefined;
}
}
setInputHasValue(newValue: boolean): void {
this.question.inputHasValue = newValue;
}

@property({ defaultValue: true }) searchEnabled: boolean;
@property({
Expand Down Expand Up @@ -170,6 +174,7 @@ export class DropdownListModel extends Base {
onBlur(event: any): void {
this.resetFilterString();
this._popupModel.isVisible = false;
this.setInputHasValue(false);
doKey2ClickBlur(event);
}
scrollToFocusedItem(): void {
Expand Down
4 changes: 4 additions & 0 deletions src/knockout/components/dropdown/dropdown.html
Expand Up @@ -15,6 +15,10 @@
'aria-describedby': question.ariaDescribedBy
},">
<div data-bind="css: question.koCss().controlValue">
<!-- ko if: question.selectedItemLocText && !question.inputHasValue -->
<!-- ko template: { name: 'survey-string', data: question.selectedItemLocText } -->
<!-- /ko -->
<!-- /ko -->
<input type="text" autocomplete="off" data-bind="
textInput: model.filterString,
css: question.cssClasses.filterStringInput,
Expand Down
8 changes: 8 additions & 0 deletions src/main.scss
Expand Up @@ -93,6 +93,7 @@ body {
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
position: relative;
}
.sv_q_dropdown_clean-button {
margin: auto 2em;
Expand All @@ -113,6 +114,13 @@ body {
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
position: absolute;
left: 0;
background-color: transparent;
}

.sv_q_tagbox__filter-string-input {
position: initial;
}

.sv_q_tagbox__placeholder {
Expand Down
3 changes: 3 additions & 0 deletions src/modern/blocks/sv-dropdown.scss
Expand Up @@ -46,6 +46,7 @@
font-family: $font-family;
font-size: inherit;
color: $text-color;
position: relative;
}
.sv-dropdown_clean-button {
padding: 3px 12px;
Expand Down Expand Up @@ -73,6 +74,8 @@
white-space: nowrap;
display: inline-block;
appearance: none;
position: absolute;
left: 0;
}
.sv-dropdown--empty:not(.sv-input--disabled) .sv-dropdown__filter-string-input::placeholder {
font-family: $font-family;
Expand Down
1 change: 1 addition & 0 deletions src/modern/blocks/sv-tagbox.scss
Expand Up @@ -57,6 +57,7 @@
width: auto;
display: flex;
flex-grow: 1;
position: initial;
}
.sv-tagbox__placeholder {
position: absolute;
Expand Down
12 changes: 11 additions & 1 deletion src/question_dropdown.ts
Expand Up @@ -154,6 +154,7 @@ export class QuestionDropdownModel extends QuestionSelectBase {
* The clean files button caption.
*/
@property({ localizable: { defaultStr: "cleanCaption" } }) cleanButtonCaption: string;
@property({ defaultValue: false }) inputHasValue: boolean;

public getControlClass(): string {
return new CssClassBuilder()
Expand All @@ -163,8 +164,17 @@ export class QuestionDropdownModel extends QuestionSelectBase {
.append(this.cssClasses.controlDisabled, this.isReadOnly)
.toString();
}
public get selectedItemLocText() {
const item = this.selectedItem;
return item?.locText;
}
public get readOnlyText() {
return this.hasOther && this.isOtherSelected ? this.otherText : (this.selectedItemText || this.placeholder);
if(this.hasOther && this.isOtherSelected) {
return this.otherText;
} else if(!!this.selectedItem) {
return this.renderAs == "select" ? this.selectedItemText : "";
}
return this.placeholder;
}
private get selectedItemText(): string {
const item = this.selectedItem;
Expand Down
3 changes: 3 additions & 0 deletions src/react/dropdown-base.tsx
Expand Up @@ -53,6 +53,8 @@ export class SurveyQuestionDropdownBase<T extends Question> extends SurveyQuesti
}

protected renderInput(dropdownListModel: DropdownListModel): JSX.Element {
const text = (this.question.selectedItemLocText && !this.question.inputHasValue) ? this.renderLocString(this.question.selectedItemLocText) : "";

const onInputChange = (e: any) => {
if (e.target === document.activeElement) {
dropdownListModel.filterString = e.target.value;
Expand All @@ -76,6 +78,7 @@ export class SurveyQuestionDropdownBase<T extends Question> extends SurveyQuesti
aria-describedby={this.question.ariaDescribedBy}
>
<div className={this.question.cssClasses.controlValue}>
{text}
<input type="text" autoComplete="off"
id={ this.question.getInputId() }
ref={(element) => (this.inputElement = element)}
Expand Down
3 changes: 2 additions & 1 deletion src/stylesmanager.ts
Expand Up @@ -879,7 +879,8 @@ export class StylesManager {
".sv_default_css .sv_q_other input:focus": "border-color: $main-color;",
".sv_default_css .sv_q_text_root:focus": "border-color: $main-color;",
".sv_default_css .sv_q_dropdown_control:focus": "border-color: $main-color;",
".sv_default_css .sv_container .sv_body .sv_p_root .sv_q .sv_q_dropdown_control .sv_q_dropdown__value .sv_q_dropdown__filter-string-input[type=text]": "border: none; outline: none; padding: 0px; width: auto; display: flex; flex-grow: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block;",
".sv_default_css .sv_container .sv_body .sv_p_root .sv_q .sv_q_dropdown_control .sv_q_dropdown__value .sv_q_dropdown__filter-string-input[type=text]": "border: none; outline: none; padding: 0px; width: auto; display: flex; flex-grow: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; position: absolute; left: 0; background-color: transparent;",
".sv_default_css .sv_container .sv_body .sv_p_root .sv_q .sv_q_dropdown_control .sv_q_dropdown__value.sv_q_tagbox__value .sv_q_dropdown__filter-string-input.sv_q_tagbox__filter-string-input": "position: initial;",
".sv_default_css input[type='text']:focus": "border-color: $main-color;",
'.sv_default_css .sv_container .sv_body .sv_p_root .sv_q input[type="radio"]:focus, .sv_default_css .sv_container .sv_body .sv_p_root .sv_q input[type="checkbox"]:focus':
"outline: 1px dotted $main-color;",
Expand Down
4 changes: 4 additions & 0 deletions src/vue/components/dropdown/dropdown.vue
Expand Up @@ -17,6 +17,10 @@
:required="question.isRequired"
>
<div :class="question.cssClasses.controlValue">
<survey-string
v-if="question.selectedItemLocText && !question.inputHasValue"
:locString="question.selectedItemLocText"
/>
<input
type="text"
ref="inputElement"
Expand Down
41 changes: 21 additions & 20 deletions testCafe/questions/dropdown.js
Expand Up @@ -4,7 +4,8 @@ const title = "dropdown";

const questionDropdownSelect = Selector(".sv_q_dropdown_control");
const listItems = Selector(".sv-list__item span");
const questionValueText = Selector(".sv_q_dropdown__value input");
const questionValueInput = Selector(".sv_q_dropdown__value input");
const questionValueText = Selector(".sv_q_dropdown__value .sv-string-viewer");
const clearButton = Selector(".sv_q_dropdown_clean-button");

frameworks.forEach((framework) => {
Expand Down Expand Up @@ -546,15 +547,15 @@ frameworks.forEach((framework) => {

const myListItems = Selector(".my-list-item");
await t
.expect(questionValueText.getAttribute("placeholder")).eql("Select...")
.expect(questionValueInput.getAttribute("placeholder")).eql("Select...")

.click(questionDropdownSelect)
.expect(myListItems.count).eql(10)
.expect(myListItems.find(".sv-svg-icon").count).eql(10)

.click(myListItems.nth(3))

.expect(questionValueText.getAttribute("placeholder")).eql("Nissan");
.expect(questionValueText.textContent).eql("Nissan");
});

test("Check dropdown key press with searchEnabled", async (t) => {
Expand Down Expand Up @@ -623,26 +624,26 @@ frameworks.forEach((framework) => {
.pressKey("down")
.pressKey("down")
.pressKey("enter")
.expect(questionValueText.getAttribute("placeholder")).eql("Nissan")
.expect(questionValueText.textContent).eql("Nissan")

.pressKey("space")
.expect(popupContainer.visible).ok()
.pressKey("up")
.pressKey("space")
.expect(popupContainer.visible).notOk()
.expect(questionValueText.getAttribute("placeholder")).eql("Volkswagen")
.expect(questionValueText.textContent).eql("Volkswagen")

.pressKey("tab")
.pressKey("2")
.pressKey("down")
.pressKey("down")
.pressKey("space")
.expect(questionValueText.nth(1).getAttribute("placeholder")).eql("item20")
.expect(questionValueText.nth(1).textContent).eql("item20")

.pressKey("down")
.pressKey("down")
.pressKey("enter")
.expect(questionValueText.nth(1).getAttribute("placeholder")).eql("item21");
.expect(questionValueText.nth(1).textContent).eql("item21");
});

test("Check dropdown key press without searchEnabled", async (t) => {
Expand Down Expand Up @@ -713,20 +714,20 @@ frameworks.forEach((framework) => {
.pressKey("down")
.pressKey("down")
.pressKey("enter")
.expect(questionValueText.getAttribute("placeholder")).eql("Nissan")
.expect(questionValueText.textContent).eql("Nissan")

.pressKey("space")
.expect(popupContainer.visible).ok()
.pressKey("up")
.pressKey("space")
.expect(popupContainer.visible).notOk()
.expect(questionValueText.getAttribute("placeholder")).eql("Volkswagen")
.expect(questionValueText.textContent).eql("Volkswagen")

.pressKey("tab")
.pressKey("down")
.pressKey("down")
.pressKey("enter")
.expect(questionValueText.nth(1).getAttribute("placeholder")).eql("item2");
.expect(questionValueText.nth(1).textContent).eql("item2");
});

test("Check dropdown search", async (t) => {
Expand Down Expand Up @@ -789,7 +790,7 @@ frameworks.forEach((framework) => {
.pressKey("down")
.pressKey("down")
.pressKey("enter")
.expect(questionValueText.getAttribute("placeholder")).eql("item25")
.expect(questionValueText.textContent).eql("item25")
.expect(popupContainer.visible).notOk()

.pressKey("down")
Expand All @@ -799,7 +800,7 @@ frameworks.forEach((framework) => {

.pressKey("down")
.pressKey("esc")
.expect(questionValueText.getAttribute("placeholder")).eql("item25");
.expect(questionValueText.textContent).eql("item25");
});

test("Check reset focused item", async (t) => {
Expand Down Expand Up @@ -913,7 +914,7 @@ frameworks.forEach((framework) => {
.expect(listItems.filterVisible().count).eql(1)

.pressKey("tab")
.expect(questionValueText.value).eql("")
.expect(questionValueInput.value).eql("")
.expect(popupContainer.visible).notOk()
.expect(clearButton.visible).notOk();
});
Expand Down Expand Up @@ -959,21 +960,20 @@ frameworks.forEach((framework) => {
}
]
};
const newDropdown = questionValueText;
const oldDropdown = Selector(".sv_q_dropdown_control").nth(1);
await initSurvey(framework, jsonWithDropDown);

await t
.expect(newDropdown.getAttribute("placeholder")).eql("Volkswagen")
.expect(questionValueText.textContent).eql("Volkswagen")
.expect(oldDropdown.value).eql("Mercedes-Benz")

.pressKey("delete")
.expect(newDropdown.getAttribute("placeholder")).eql("Select...")
.expect(questionValueInput.getAttribute("placeholder")).eql("Select...")
.expect(oldDropdown.value).eql("Mercedes-Benz")

.pressKey("tab")
.pressKey("delete")
.expect(newDropdown.getAttribute("placeholder")).eql("Select...")
.expect(questionValueInput.getAttribute("placeholder")).eql("Select...")
.expect(oldDropdown.value).eql("");
});

Expand Down Expand Up @@ -1101,16 +1101,17 @@ frameworks.forEach((framework) => {
]
};
const ratingAsDropdownPlaceHolder = "Tap to rate here...";
const ratingAsDropdown = Selector(".sd-dropdown .sd-dropdown__value input");
const ratingAsDropdown = Selector(".sd-dropdown .sd-dropdown__value");
const ratingAsDropdownText = ratingAsDropdown.find("input");
await initSurvey(framework, jsonWithDropDown);

await t
.click(ratingAsDropdown)
.click(getListItemByText("2"))
.expect(ratingAsDropdown.getAttribute("placeholder")).contains("2")
.expect(ratingAsDropdownText.getAttribute("placeholder")).eql("2")

.pressKey("delete")
.expect(ratingAsDropdown.getAttribute("placeholder")).contains(ratingAsDropdownPlaceHolder);
.expect(ratingAsDropdownText.getAttribute("placeholder")).eql(ratingAsDropdownPlaceHolder);
});
test.page(`${url_test}${theme}/${framework}.html`)("Check dropdown popup width", async (t) => {
await applyTheme(theme);
Expand Down

0 comments on commit 6e7e13f

Please sign in to comment.