diff --git a/src/defaultCss/cssstandard.ts b/src/defaultCss/cssstandard.ts index c4a071456c..691b6147c6 100644 --- a/src/defaultCss/cssstandard.ts +++ b/src/defaultCss/cssstandard.ts @@ -305,6 +305,7 @@ export var defaultStandardCss = { rating: { root: "sv_q_rating", item: "sv_q_rating_item", + itemFixedSize: "sv_q_rating_item_fixed", selected: "active", minText: "sv_q_rating_min_text", itemText: "sv_q_rating_item_text", diff --git a/src/defaultCss/defaultV2Css.ts b/src/defaultCss/defaultV2Css.ts index 994e97297b..3ba8067fae 100644 --- a/src/defaultCss/defaultV2Css.ts +++ b/src/defaultCss/defaultV2Css.ts @@ -461,6 +461,7 @@ export var defaultV2Css = { itemText: "sd-rating__item-text", maxText: "sd-rating__item-text sd-rating__max-text", itemDisabled: "sd-rating__item--disabled", + itemFixedSize: "sd-rating__item--fixed-size", control: "sd-input sd-dropdown", controlValue: "sd-dropdown__value", controlDisabled: "sd-input--disabled", diff --git a/src/defaultV2-theme/blocks/sd-rating.scss b/src/defaultV2-theme/blocks/sd-rating.scss index 20799313f6..687f4764fc 100644 --- a/src/defaultV2-theme/blocks/sd-rating.scss +++ b/src/defaultV2-theme/blocks/sd-rating.scss @@ -25,7 +25,11 @@ box-shadow: 0px 1px 2px $shadow-inner; border-radius: calcSize(12.5); white-space: nowrap; - padding: calcSize(1.25); + padding: calcSize(0.5) calcSize(2.5); + height: calcSize(6); + display: flex; + justify-content: center; + align-items: center; box-sizing: border-box; min-width: calcSize(6); text-align: center; @@ -34,6 +38,10 @@ fill: $foreground; font-size: calcSize(2); } +.sd-rating__item--fixed-size { + width: calcSize(6); + padding: 0; +} legend+.sd-rating__item, legend+sv-ng-rating-item+.sd-rating__item { @@ -230,7 +238,6 @@ legend+sv-ng-rating-item+.sd-rating__item-smiley { .sd-rating__item-text.sd-rating__item-text { font-size: calcSize(2); line-height: calcSize(3); - min-width: calcSize(3); display: inline-block; &.sd-rating__min-text, @@ -255,4 +262,7 @@ legend+sv-ng-rating-item+.sd-rating__item-smiley { .sv-string-editor { white-space: nowrap; } + &.sd-rating__item--fixed-size { + min-width: calcSize(3); + } } \ No newline at end of file diff --git a/src/question_rating.ts b/src/question_rating.ts index a36de6cd82..0cda76fb3d 100644 --- a/src/question_rating.ts +++ b/src/question_rating.ts @@ -12,6 +12,9 @@ import { mergeValues } from "./utils/utils"; import { DropdownListModel } from "./dropdownListModel"; export class RenderedRatingItem extends Base { + private onStringChangedCallback() { + this.text = this.itemValue.text; + } public get value(): number { return this.itemValue.getPropertyValue("value"); } @@ -20,11 +23,11 @@ export class RenderedRatingItem extends Base { public get locText(): LocalizableString { return this.locString || this.itemValue.locText; } - public get text(): string { - return this.itemValue.text; - } + @property({ defaultValue: "" }) text: string; constructor(public itemValue: ItemValue, private locString: LocalizableString = null) { super(); + this.locText.onStringChanged.add(this.onStringChangedCallback.bind(this)); + this.onStringChangedCallback(); } } @@ -388,6 +391,16 @@ export class QuestionRatingModel extends Question { itemitemOnErrorClass = this.cssClasses.itemSmileyOnError; } + const hasFixedSize = + !this.isStar && + !this.isSmiley && + (!this.displayRateDescriptionsAsExtremeItems || + this.rateValues.length > 0 && item != this.rateValues[0] && item != this.rateValues[this.rateValues.length - 1] || + this.rateValues.length == 0 && item.value != this.rateMin && item.value != this.rateMax + ) && + item.locText.calculatedText.length <= 2 && + Number.isInteger(Number(item.locText.calculatedText)); + return new CssClassBuilder() .append(itemClass) .append(itemSelectedClass, isSelected) @@ -396,6 +409,7 @@ export class QuestionRatingModel extends Question { .append(itemHighlightedClass, isHighlighted) .append(itemUnhighlightedClass, isUnhighlighted) .append(itemitemOnErrorClass, this.errors.length > 0) + .append(this.cssClasses.itemFixedSize, hasFixedSize) .toString(); } //methods for mobile view diff --git a/testCafe/questions/rating.js b/testCafe/questions/rating.js index 1c3d319feb..c2d1c83c6b 100644 --- a/testCafe/questions/rating.js +++ b/testCafe/questions/rating.js @@ -135,6 +135,26 @@ frameworks.forEach((framework) => { await t.expect(Selector(".sd-question select").visible).ok; }); + test("check fixed width observability", async (t) => { + var json = { + questions: [ + { + "type": "rating", + "name": "q1", + "rateValues": [ + 0, + 1, + 2, + ] + }, + ], + }; + await initSurvey(framework, json); + await t.expect(Selector(".sv_q_rating_item").withText("1").classNames).contains("sv_q_rating_item_fixed"); + await ClientFunction(() => { window["survey"].getQuestionByName("q1").rateValues[0].value = "a"; })(); + await t.expect(Selector(".sv_q_rating_item").withText("a").classNames).notContains("sv_q_rating_item_fixed"); + }); + const jsonStars = { questions: [ { diff --git a/tests/question_ratingtests.ts b/tests/question_ratingtests.ts index 624c0b2fd5..75f5abf835 100644 --- a/tests/question_ratingtests.ts +++ b/tests/question_ratingtests.ts @@ -546,4 +546,66 @@ QUnit.test("check smileys styles", (assert) => { assert.equal(q1.getItemClass(q1.renderedRateItems[2].itemValue), "sv_q_disabled"); assert.equal(q1.getItemClass(q1.renderedRateItems[3].itemValue), "sv_q_disabled"); assert.equal(q1.getItemClass(q1.renderedRateItems[4].itemValue), "sv_q_disabled"); -}); \ No newline at end of file +}); + +QUnit.test("check fixed width styles", (assert) => { + var json = { + questions: [ + { + "type": "rating", + "name": "q1", + "rateMin": 0, + "rateMax": 4, + "minRateDescription": "mindesc", + "maxRateDescription": "maxdesc", + "displayRateDescriptionsAsExtremeItems": true + }, + ], + }; + const survey = new SurveyModel(json); + const q1 = survey.getQuestionByName("q1"); + q1.cssClasses.item = "sv_q_item"; + q1.cssClasses.itemHover = ""; + q1.cssClasses.itemFixedSize = "sv_q_item-fixed"; + + assert.equal(q1.getItemClass(q1.renderedRateItems[0].itemValue), "sv_q_item"); + assert.equal(q1.getItemClass(q1.renderedRateItems[1].itemValue), "sv_q_item sv_q_item-fixed"); + assert.equal(q1.getItemClass(q1.renderedRateItems[2].itemValue), "sv_q_item sv_q_item-fixed"); + assert.equal(q1.getItemClass(q1.renderedRateItems[3].itemValue), "sv_q_item sv_q_item-fixed"); + assert.equal(q1.getItemClass(q1.renderedRateItems[4].itemValue), "sv_q_item"); +}); + +QUnit.test("check fixed width styles - rate values", (assert) => { + var json = { + questions: [ + { + "type": "rating", + "name": "q1", + "rateValues": [ + 0, + 1, + { + "value": 2, + "text": "middle" + }, + 3, + 4 + ], + "minRateDescription": "mindesc", + "maxRateDescription": "maxdesc", + "displayRateDescriptionsAsExtremeItems": true + }, + ], + }; + const survey = new SurveyModel(json); + const q1 = survey.getQuestionByName("q1"); + q1.cssClasses.item = "sv_q_item"; + q1.cssClasses.itemHover = ""; + q1.cssClasses.itemFixedSize = "sv_q_item-fixed"; + + assert.equal(q1.getItemClass(q1.renderedRateItems[0].itemValue), "sv_q_item"); + assert.equal(q1.getItemClass(q1.renderedRateItems[1].itemValue), "sv_q_item sv_q_item-fixed"); + assert.equal(q1.getItemClass(q1.renderedRateItems[2].itemValue), "sv_q_item"); + assert.equal(q1.getItemClass(q1.renderedRateItems[3].itemValue), "sv_q_item sv_q_item-fixed"); + assert.equal(q1.getItemClass(q1.renderedRateItems[4].itemValue), "sv_q_item"); +}); diff --git a/visualRegressionTests/tests/defaultV2/etalons/question-rating-long-items.png b/visualRegressionTests/tests/defaultV2/etalons/question-rating-long-items.png index cfacdae50d..337f605196 100644 Binary files a/visualRegressionTests/tests/defaultV2/etalons/question-rating-long-items.png and b/visualRegressionTests/tests/defaultV2/etalons/question-rating-long-items.png differ