diff --git a/src/actions/action.ts b/src/actions/action.ts index f249857f0a..b1b9b9c781 100644 --- a/src/actions/action.ts +++ b/src/actions/action.ts @@ -312,6 +312,9 @@ export abstract class BaseAction extends Base implements IAction { !!this.title ); } + public get hasSubItems(): boolean { + return !!this.items && this.items.length > 0; + } public getActionBarItemTitleCss(): string { return new CssClassBuilder() .append(this.cssClasses.itemTitle) @@ -432,12 +435,14 @@ export class Action extends BaseAction implements IAction, ILocalizableOwner { private createLocTitle(): LocalizableString { return this.createLocalizableString("title", this, true); } - public setItems(items: Array, onSelectionChanged?: (item: Action, ...params: any[]) => void): void { + public setSubItems(options: IListModel): void { this.markerIconName = "icon-next_16x16"; this.component = "sv-list-item-group"; - this.items = [...items]; + this.items = [...options.items]; + const listOptions = Object.assign({}, options); + listOptions.searchEnabled = false; const popupModel = createPopupModelWithListModel( - { items: items, onSelectionChanged: onSelectionChanged, searchEnabled: false }, + listOptions, { horizontalPosition: "right", showPointer: false, canShrink: false } ); popupModel.cssClass = "sv-popup-inner"; diff --git a/src/common-styles/sv-list.scss b/src/common-styles/sv-list.scss index cb94ed9add..5cad48437e 100644 --- a/src/common-styles/sv-list.scss +++ b/src/common-styles/sv-list.scss @@ -177,6 +177,18 @@ li:focus .sv-list__item.sv-list__item--selected { } } +.sv-list__item.sv-list__item--selected.sv-list__item--group { + & > .sv-list__item-body { + background-color: $primary-light; + color: $font-questiontitle-color; + font-weight: 400; + + use { + fill: $foreground-light; + } + } +} + .sv-list__item.sv-list__item--disabled { .sv-list__item-body { cursor: default; diff --git a/src/list.ts b/src/list.ts index 62315532f4..0a68d6ec4a 100644 --- a/src/list.ts +++ b/src/list.ts @@ -13,6 +13,7 @@ export let defaultListCss = { searchClearButtonIcon: "sv-list__filter-clear-button", loadingIndicator: "sv-list__loading-indicator", itemSelected: "sv-list__item--selected", + itemGroup: "sv-list__item--group", itemWithIcon: "sv-list__item--with-icon", itemDisabled: "sv-list__item--disabled", itemFocused: "sv-list__item--focused", @@ -243,6 +244,7 @@ export class ListModel extends ActionContainer .append(this.cssClasses.itemDisabled, this.isItemDisabled(itemValue)) .append(this.cssClasses.itemFocused, this.isItemFocused(itemValue)) .append(this.cssClasses.itemSelected, this.isItemSelected(itemValue)) + .append(this.cssClasses.itemGroup, itemValue.hasSubItems) .append(this.cssClasses.itemHovered, itemValue.isHovered) .append(this.cssClasses.itemTextWrap, this.textWrapEnabled) .append(itemValue.css) diff --git a/testCafe/components/popup.ts b/testCafe/components/popup.ts index c64eb7b014..889f42bdcb 100644 --- a/testCafe/components/popup.ts +++ b/testCafe/components/popup.ts @@ -82,9 +82,9 @@ function addDropdownActionWithSubItems(_, opt) { for (let index = 0; index < 40; index++) { items[index] = new window["Survey"].Action({ id: index, title: "item" + index }); } - items[5].setItems([...subitems], (item) => { let value = item.id; }); + items[5].setSubItems({ items: [...subitems] }); items[5].title += " has items"; - items[6].setItems([...subitems], (item) => { let value = item.id; }); + items[6].setSubItems({ items: [...subitems] }); items[6].title += " has items"; const dropdownWithSearchAction = window["Survey"].createDropdownActionModel( diff --git a/tests/components/actionbartests.ts b/tests/components/actionbartests.ts index 17dc14c15e..983fd0bd72 100644 --- a/tests/components/actionbartests.ts +++ b/tests/components/actionbartests.ts @@ -295,19 +295,19 @@ QUnit.test("Action locTitleName doesn't work correctly, bug#8093", (assert) => { survey.locale = "fr"; assert.equal(action1.title, "Effacer la page", "Clear page fr#2"); }); -QUnit.test("Action subitems popup canShrink property", function (assert) { +QUnit.test("Action setSubItems popup canShrink property", function (assert) { const action = new Action({ id: "test2", title: "test2" }); const subitems = [new Action({ id: "test28", title: "test28" }), new Action({ id: "test29", title: "test29" })]; - (action as Action).setItems(subitems, () => { }); + (action as Action).setSubItems({ items: subitems }); assert.notOk(action.popupModel.canShrink, "popub model for subitems should not shrink"); }); -QUnit.test("Action setItems popup canShrink property", function (assert) { +QUnit.test("Action setSubItems popup item click", function (assert) { const action = new Action({ id: "test2", title: "test2" }); const subitems = [new Action({ id: "test28", title: "test28" }), new Action({ id: "test29", title: "test29" })]; let event = ""; - (action as Action).setItems(subitems, (item) => { event = item.title; }); + (action as Action).setSubItems({ items: subitems, onSelectionChanged: (item) => { event = item.title; } }); action.popupModel.contentComponentData.model.onItemClick(action.items[1]); assert.equal(event, "test29"); }); \ No newline at end of file diff --git a/tests/listModelTests.ts b/tests/listModelTests.ts index ea316ba61e..bbdfe2b953 100644 --- a/tests/listModelTests.ts +++ b/tests/listModelTests.ts @@ -415,7 +415,7 @@ QUnit.test("ListModel search in subitems", function (assert) { } const subitems = [new Action({ id: "test28", title: "test28" }), new Action({ id: "test29", title: "test29" })]; - (items[2] as Action).setItems(subitems, () => { }); + (items[2] as Action).setSubItems({ items: subitems }); const list = new ListModel(items, () => { }, true); let filteredActions; filteredActions = list.renderedActions.filter(item => list.isItemVisible(item)); diff --git a/tests/markup/etalon_components.ts b/tests/markup/etalon_components.ts index 57a29f1737..1e1f60595a 100644 --- a/tests/markup/etalon_components.ts +++ b/tests/markup/etalon_components.ts @@ -50,7 +50,7 @@ registerMarkupTests( verticalPosition: "bottom", horizontalPosition: "center", items: items, onSelectionChanged: (item, ...params) => { } }); - items[1].setItems([{ id: "11", title: "text11" }, { id: "21", title: "text21" }], () => { }); + items[1].setSubItems({ items: [{ id: "11", title: "text11" }, { id: "21", title: "text21" }] }); opt.titleActions = [item]; }); }, diff --git a/tests/markup/snapshots/list-component-with-subitems.snap.html b/tests/markup/snapshots/list-component-with-subitems.snap.html index 48a67c636e..e7b800b501 100644 --- a/tests/markup/snapshots/list-component-with-subitems.snap.html +++ b/tests/markup/snapshots/list-component-with-subitems.snap.html @@ -14,7 +14,7 @@ text1 -
  • +
  • text2 diff --git a/visualRegressionTests/tests/defaultV2/etalons/popup-with-subitems-with-selected-elements.png b/visualRegressionTests/tests/defaultV2/etalons/popup-with-subitems-with-selected-elements.png new file mode 100644 index 0000000000..d921430001 Binary files /dev/null and b/visualRegressionTests/tests/defaultV2/etalons/popup-with-subitems-with-selected-elements.png differ diff --git a/visualRegressionTests/tests/defaultV2/popup.ts b/visualRegressionTests/tests/defaultV2/popup.ts index 732b89ac83..77803343f7 100644 --- a/visualRegressionTests/tests/defaultV2/popup.ts +++ b/visualRegressionTests/tests/defaultV2/popup.ts @@ -201,9 +201,9 @@ function addDropdownActionWithSubItems(_, opt) { for (let index = 0; index < 10; index++) { items[index] = new window["Survey"].Action({ id: index, title: "item" + index }); } - items[5].setItems([...subitems], (item) => { let value = item.id; }); + items[5].setSubItems({ items: [...subitems] }); items[5].title += " has items"; - items[6].setItems([...subitems], (item) => { let value = item.id; }); + items[6].setSubItems({ items: [...subitems] }); items[6].title += " has items"; const dropdownWithSearchAction = window["Survey"].createDropdownActionModel( @@ -221,6 +221,37 @@ function addDropdownActionWithSubItems(_, opt) { opt.titleActions = [dropdownWithSearchAction]; } +function addDropdownActionWithSubItemsAndSelectedItems(_, opt) { + let subitems: Array = []; + for (let index = 0; index < 7; index++) { + subitems[index] = { id: index, title: "inner item" + index }; + } + + let items: Array = []; + for (let index = 0; index < 10; index++) { + items[index] = new window["Survey"].Action({ id: index, title: "item" + index }); + } + items[5].setSubItems({ items: [...subitems], selectedItem: subitems[3] }); + items[5].title += " has items"; + items[6].setSubItems({ items: [...subitems] }); + items[6].title += " has items"; + + const dropdownWithSearchAction = window["Survey"].createDropdownActionModel( + { title: "Subitems", showTitle: true }, + { + items: items, + showPointer: true, + verticalPosition: "bottom", + horizontalPosition: "center", + selectedItem: items[5], + onSelectionChanged: (item, ...params) => { + let value = item.id; + } + } + ); + opt.titleActions = [dropdownWithSearchAction]; +} + frameworks.forEach(framework => { fixture`${framework} ${title} ${theme}` .page`${url_test}${theme}/${framework}` @@ -497,7 +528,24 @@ frameworks.forEach(framework => { .hover(item5) // show item5Subitems; .wait(300); await takeElementScreenshot("popup-with-subitems-left.png", titlePopup, t, comparer); + }); + }); + test("Popup with subitems and selected elements", async t => { + await wrapVisualTest(t, async (t, comparer) => { + await t.resizeWindow(1300, 650); + await initSurvey(framework, json, { + onGetQuestionTitleActions: addDropdownActionWithSubItemsAndSelectedItems + }); + + const titlePopup = Selector(".sv-popup.sv-popup--show-pointer"); + const item5 = getListItemByText("item5 has items").find(".sv-list__item-body"); + + await t + .click(Selector(".sv-action")) // show action popup + .hover(item5) // show item5Subitems + .wait(300); + await takeElementScreenshot("popup-with-subitems-with-selected-elements.png", titlePopup, t, comparer); }); }); }); \ No newline at end of file