diff --git a/packages/main/src/ColorPalette.ts b/packages/main/src/ColorPalette.ts index 25594759b10f..8a3eac219828 100644 --- a/packages/main/src/ColorPalette.ts +++ b/packages/main/src/ColorPalette.ts @@ -213,6 +213,16 @@ class ColorPalette extends UI5Element { } } + onAfterRendering() { + if (this.popupMode) { + if (this.showDefaultColor) { + this.focusFirstFocusableElement(); + } else { + this.focusFirstDisplayColorElement(); + } + } + } + selectColor(item: ColorPaletteItem) { if (!item.value) { return; @@ -306,7 +316,7 @@ class ColorPalette extends UI5Element { if (this.hasRecentColors) { this.focusColorElement(this.colorPaletteNavigationElements[index + 1], this._itemNavigationRecentColors); } else if (this.showDefaultColor) { - this.colorPaletteNavigationElements[0].focus(); + this.firstFocusableElement.focus(); } else { this.focusColorElement(this.displayedColors[0], this._itemNavigation); } @@ -324,7 +334,7 @@ class ColorPalette extends UI5Element { if (isUp(e) && target === this.displayedColors[0] && this.colorPaletteNavigationElements.length > 1) { e.stopPropagation(); if (this.showDefaultColor) { - this.colorPaletteNavigationElements[0].focus(); + this.firstFocusableElement.focus(); } else if (!this.showDefaultColor && this.hasRecentColors) { this.focusColorElement(lastElementInNavigation, this._itemNavigationRecentColors); } else if (!this.showDefaultColor && this.showMoreColors) { @@ -337,7 +347,7 @@ class ColorPalette extends UI5Element { if (this.showDefaultColor && this.showMoreColors) { this.colorPaletteNavigationElements[2].focus(); } else if (this.showDefaultColor && !this.showMoreColors && (!this.showRecentColors || !this.recentColors[0])) { - this.colorPaletteNavigationElements[0].focus(); + this.firstFocusableElement.focus(); } else if (isRecentColorsNextElement) { this.focusColorElement(lastElementInNavigation, this._itemNavigationRecentColors); } else if (!this.showDefaultColor && this.showMoreColors) { @@ -359,7 +369,7 @@ class ColorPalette extends UI5Element { } } else if (isDown(e)) { if (this.showDefaultColor) { - this.colorPaletteNavigationElements[0].focus(); + this.firstFocusableElement.focus(); } else { e.stopPropagation(); this.focusColorElement(this.displayedColors[0], this._itemNavigation); @@ -372,6 +382,18 @@ class ColorPalette extends UI5Element { itemNavigation._focusCurrentItem(); } + focusFirstDisplayColorElement() { + this.focusColorElement(this.displayedColors[0], this._itemNavigation); + } + + focusFirstFocusableElement() { + this.firstFocusableElement.focus(); + } + + get firstFocusableElement() { + return this.colorPaletteNavigationElements[0]; + } + async _chooseCustomColor() { const colorPicker = await this.getColorPicker(); this._setColor(colorPicker.color); diff --git a/packages/main/src/ColorPalettePopover.hbs b/packages/main/src/ColorPalettePopover.hbs index 37ad23b7d953..d4dac278cda3 100644 --- a/packages/main/src/ColorPalettePopover.hbs +++ b/packages/main/src/ColorPalettePopover.hbs @@ -2,6 +2,9 @@ hide-arrow content-only-on-desktop placement-type="Bottom" + ?open="{{_open}}" + .opener="{{opener}}" + @ui5-after-close="{{onAfterClose}}" >
ui5-color-palette-popover is closed due to user interaction. + * @event sap.ui.webc.main.ColorPalettePopover#close + * @since 1.21.0 + * @public + */ +@event("close") class ColorPalettePopover extends UI5Element { /** * Defines whether the user can see the last used colors in the bottom of the component @@ -124,6 +132,28 @@ class ColorPalettePopover extends UI5Element { @property({ validator: CSSColor }) defaultColor?: string; + /** + * Defines the open | closed state of the popover. + * @public + * @type {boolean} + * @name sap.ui.webc.main.ColorPalettePopover.prototype.open + * @defaultvalue false + * @since 1.21.0 + */ + @property({ type: Boolean }) + open!: boolean; + + /** + * Defines the ID or DOM Reference of the element that the popover is shown at. + * @public + * @type {sap.ui.webc.base.types.DOMReference} + * @name sap.ui.webc.main.ColorPalettePopover.prototype.opener + * @defaultvalue undefined + * @since 1.21.0 + */ + @property({ validator: DOMReference }) + opener?: HTMLElement | string; + /** * Defines the content of the component. * @type {sap.ui.webc.main.IColorPaletteItem[]} @@ -136,8 +166,6 @@ class ColorPalettePopover extends UI5Element { static i18nBundle: I18nBundle; - responsivePopover?: ResponsivePopover; - static async onDefine() { ColorPalettePopover.i18nBundle = await getI18nBundle("@ui5/webcomponents"); } @@ -146,13 +174,12 @@ class ColorPalettePopover extends UI5Element { super(); } - _respPopover() { - this.responsivePopover = this.shadowRoot!.querySelector("[ui5-responsive-popover]")!; - return this.responsivePopover; + get responsivePopover() { + return this.shadowRoot!.querySelector("[ui5-responsive-popover]")!; } - _colorPalette() { - return this.responsivePopover!.content[0].querySelector("[ui5-color-palette]")!; + get respPopover() { + return this.shadowRoot!.querySelector("[ui5-responsive-popover]")!; } /** @@ -161,10 +188,13 @@ class ColorPalettePopover extends UI5Element { * @public * @method * @name sap.ui.webc.main.ColorPalettePopover#showAt + * @deprecated The method is deprecated in favour of open and opener properties. * @since 1.1.1 */ showAt(opener: HTMLElement) { - this._openPopover(opener); + console.warn("The method 'showAt' is deprecated and will be removed in future, use 'open' and 'opener' props instead."); // eslint-disable-line + this.open = true; + this.opener = opener; } /** @@ -175,27 +205,20 @@ class ColorPalettePopover extends UI5Element { * @method * @name sap.ui.webc.main.ColorPalettePopover#openPopover * @since 1.0.0-rc.16 - * @deprecated The method is deprecated in favour of showAt. + * @deprecated The method is deprecated in favour of open and opener properties. */ openPopover(opener: HTMLElement) { - console.warn("The method 'openPopover' is deprecated and will be removed in future, use 'showAt' instead."); // eslint-disable-line - this._openPopover(opener); + console.warn("The method 'openPopover' is deprecated and will be removed in future, use 'open' and 'opener' props instead."); // eslint-disable-line + this.showAt(opener); } - _openPopover(opener: HTMLElement) { - this._respPopover(); - - this.responsivePopover!.showAt(opener, true); - - if (this.showDefaultColor) { - this._colorPalette().colorPaletteNavigationElements[0].focus(); - } else { - this._colorPalette().focusColorElement(this._colorPalette().colorPaletteNavigationElements[0], this._colorPalette()._itemNavigation); - } + closePopover() { + this.open = false; } - closePopover() { - this.responsivePopover!.close(); + onAfterClose() { + this.closePopover(); + this.fireEvent("close"); } onSelectedColor(e: CustomEvent) { @@ -211,8 +234,7 @@ class ColorPalettePopover extends UI5Element { * @returns {boolean} */ isOpen() { - this._respPopover(); - return this.responsivePopover!.opened; + return this.open; } get colorPaletteColors() { @@ -226,6 +248,10 @@ class ColorPalettePopover extends UI5Element { get _cancelButtonLabel() { return ColorPalettePopover.i18nBundle.getText(COLOR_PALETTE_DIALOG_CANCEL_BUTTON); } + + get _open() { + return this.open || undefined; + } } ColorPalettePopover.define(); diff --git a/packages/main/src/Popover.ts b/packages/main/src/Popover.ts index 45faa6bbefd9..b7d550b1a3be 100644 --- a/packages/main/src/Popover.ts +++ b/packages/main/src/Popover.ts @@ -298,7 +298,7 @@ class Popover extends Popup { if (this.opener instanceof HTMLElement) { opener = this.opener; } else if (typeof this.opener === "string") { - opener = (this.getRootNode() as Document).getElementById(this.opener); + opener = (this.getRootNode() as Document).getElementById(this.opener) || document.getElementById(this.opener); } if (!opener) { diff --git a/packages/main/src/themes/ColorPalettePopover.css b/packages/main/src/themes/ColorPalettePopover.css index c2fb72a91c19..59461ca0029a 100644 --- a/packages/main/src/themes/ColorPalettePopover.css +++ b/packages/main/src/themes/ColorPalettePopover.css @@ -16,6 +16,10 @@ padding: 0; } +[ui5-title], [ui5-button] { + margin-bottom: 1rem; +} + .ui5-cp-item-container { padding: 0.3125rem 0.6875rem; } \ No newline at end of file diff --git a/packages/main/test/pages/ColorPalettePopover.html b/packages/main/test/pages/ColorPalettePopover.html index f4a18de1e652..f93e5cc63b47 100644 --- a/packages/main/test/pages/ColorPalettePopover.html +++ b/packages/main/test/pages/ColorPalettePopover.html @@ -4,25 +4,86 @@ Color Palette Popover - - - - - - - Open ColorPalettePopover - + + Test case 1 - Default Color Button to be focused + Open + + + + + + + + + + + + + + + + Test case 2 - Default Color works + Open + + + + + + + + + + + + + + + + Test case 3 - Arrow Down to select displayed color + Open + + + + + + + + + + + + + + + + + Test case 4 - Arrow Up focuses MoreColors button + Open + + + + + + + + + + + + + + + + Test case 5 - Tests navigation with recent colors + Open + @@ -37,8 +98,11 @@ - Open ColorPalettePopover - + Test case 6 - close + Open + + Press + @@ -53,8 +117,9 @@ - Open ColorPalettePopover - + Only Colors + Open + @@ -69,8 +134,9 @@ - Open ColorPalettePopover - + show-default-color + Open + @@ -85,8 +151,9 @@ - Open ColorPalettePopover - + show-more-colors and show-default-color + Open + @@ -101,8 +168,9 @@ - Open ColorPalettePopover - + show-recent-colors, show-more-colors and show-default-color + Open + @@ -116,24 +184,53 @@ + diff --git a/packages/main/test/specs/ColorPalettePopover.spec.js b/packages/main/test/specs/ColorPalettePopover.spec.js index 7e4088df2c4c..488a30ef2387 100644 --- a/packages/main/test/specs/ColorPalettePopover.spec.js +++ b/packages/main/test/specs/ColorPalettePopover.spec.js @@ -6,92 +6,148 @@ describe("ColorPalette interactions", () => { }); it("Test if focusing first element works on initial open", async () => { - await browser.url(`test/pages/ColorPalettePopover.html`); - const colorPaletteButton = await browser.$("#colorPaletteBtn"); + const colorPaletteButton = await browser.$("#colorPaletteBtnTest"); + + // act - open color palette popover await colorPaletteButton.click(); - const colorPalettePopover = await browser.$("[ui5-color-palette-popover]"); + + const colorPalettePopover = await browser.$("#colorPalettePopoverTest"); const responsivePopover = await colorPalettePopover.shadow$("[ui5-responsive-popover]") const colorPalette = await responsivePopover.$("[ui5-color-palette]"); const defaultButton = await colorPalette.shadow$(".ui5-cp-default-color-button"); - assert.ok(await defaultButton.getProperty("focused"), "Check if the first element is focused"); + // assert - default btn is focused + assert.ok(await defaultButton.getProperty("focused"), "The first element is focused"); + + // act - close popover + await defaultButton.click(); }); it("Test if default color functionality works", async () => { - await browser.url(`test/pages/ColorPalettePopover.html`); + const DEFAULT_COLOR = "green"; + const colorPaletteButton = await browser.$("#colorPaletteBtnTest2"); - const colorPaletteButton = await browser.$("#colorPaletteBtn"); + // act - open color palette popover await colorPaletteButton.click(); - const colorPalettePopover = await browser.$("[ui5-color-palette-popover]"); + + const colorPalettePopover = await browser.$("#colorPalettePopoverTest2"); const colorPalette = await colorPalettePopover.shadow$("[ui5-responsive-popover]").$("[ui5-color-palette]"); const defaultButton = await colorPalette.shadow$(".ui5-cp-default-color-button"); + // act - activate default color await defaultButton.keys("Space"); - assert.strictEqual(await colorPalette.getProperty("selectedColor"), "green", "Check if selected value is darkgreen"); + // assert - "green" is selected as default color + assert.strictEqual(await colorPalette.getProperty("selectedColor"), DEFAULT_COLOR, "The selected value is green"); }); it("Test if keyboard navigation on elements works", async () => { - await browser.url(`test/pages/ColorPalettePopover.html`); - const colorPaletteButton = await browser.$("#colorPaletteBtn"); + const EXPTECTED_COLOR = "pink" + const colorPaletteButton = await browser.$("#colorPaletteBtnTest3"); + + // act - open color palette popover await colorPaletteButton.click(); - const colorPalettePopover = await browser.$("[ui5-color-palette-popover]"); + + const colorPalettePopover = await browser.$("#colorPalettePopoverTest3"); const colorPalette = await colorPalettePopover.shadow$("[ui5-responsive-popover]").$("[ui5-color-palette]"); const colorPaletteEntries = await colorPalette.shadow$$("[ui5-color-palette-item]"); const defaultButton = await colorPalette.shadow$(".ui5-cp-default-color-button"); const item = colorPaletteEntries[0]; + // act - navigate to a color with "ArrowDown" await defaultButton.keys("ArrowDown"); await item.keys("Space"); - assert.strictEqual(await colorPalette.getProperty("selectedColor"), "pink", "Check if selected value is pink"); + // assert - "pink" is selected with "SPACE" + assert.strictEqual(await colorPalette.getProperty("selectedColor"), EXPTECTED_COLOR, "The selected value is pink"); }); it("Test if keyboard navigation on elements works", async () => { - await browser.url(`test/pages/ColorPalettePopover.html`); - const colorPaletteButton = await browser.$("#colorPaletteBtn"); + const colorPaletteButton = await browser.$("#colorPaletteBtnTest4"); + + // act - open color palette popover await colorPaletteButton.click(); - const colorPalettePopover = await browser.$("[ui5-color-palette-popover]"); + + const colorPalettePopover = await browser.$("#colorPalettePopoverTest4"); const colorPalette = await colorPalettePopover.shadow$("[ui5-responsive-popover]").$("[ui5-color-palette]"); const moreColorsButton = await colorPalette.shadow$(".ui5-cp-more-colors"); const defaultButton = await colorPalette.shadow$(".ui5-cp-default-color-button"); await defaultButton.keys("ArrowUp"); - assert.ok(await moreColorsButton.getProperty("focused"), "Check if more colors button is focused"); + // assert - MoreColors button is focused + assert.ok(await moreColorsButton.getProperty("focused"), "Button 'MoreColors' is focused"); + + // act - close popover + await defaultButton.click(); }); it("Tests navigation with recent colors", async () => { - await browser.url(`test/pages/ColorPalettePopover.html`); - - const colorPaletteButton = await browser.$("#colorPaletteBtn"); + const colorPaletteButton = await browser.$("#colorPaletteBtnTest5"); + + // act - open color palette popover await colorPaletteButton.click(); - const colorPalettePopover = await browser.$("[ui5-color-palette-popover]"); + + const colorPalettePopover = await browser.$("#colorPalettePopoverTest5"); const colorPalette = await colorPalettePopover.shadow$("[ui5-responsive-popover]").$("[ui5-color-palette]"); const defaultButton = await colorPalette.shadow$(".ui5-cp-default-color-button"); const moreColorsButton = await colorPalette.shadow$(".ui5-cp-more-colors"); const firstRecentColorsElement = await colorPalette.shadow$(".ui5-cp-recent-colors-container [ui5-color-palette-item]"); + // act - press default color btn and re-open color palette popover await defaultButton.keys("Space"); - await colorPaletteButton.click(); + // act - navigate to recent colors await defaultButton.keys("ArrowUp"); await firstRecentColorsElement.keys("ArrowUp"); + // assert - MoreColors is focused assert.ok(await moreColorsButton.getProperty("focused"), "Check if more colors button is focused"); + + // act - close popover + await defaultButton.click(); + }); + + it("Test 'close' event fired when popover closes", async () => { + const colorPaletteButton = await browser.$("#colorPaletteBtnTest6"); + const btnFocusOut = await browser.$("#btnFocusOut"); + const inpOpenChangeCounter = await browser.$("#inpOpenChangeCounter"); + + // act - open color palette popover and click outside + await colorPaletteButton.click(); + await btnFocusOut.click(); + + // assert + assert.ok(await inpOpenChangeCounter.getProperty("value"), "1", "Event 'close' fired when popover closes."); + + // act - open color palette popover + await colorPaletteButton.click(); + + const colorPalettePopover = await browser.$("#colorPalettePopoverTest6"); + const colorPalette = await colorPalettePopover.shadow$("[ui5-responsive-popover]").$("[ui5-color-palette]"); + const defaultButton = await colorPalette.shadow$(".ui5-cp-default-color-button"); + + // act - select default color + await defaultButton.click(); + + // assert + assert.ok(await inpOpenChangeCounter.getProperty("value"), "2", "Event 'close' fired when popover closes."); }); it("Test attribute propagation propagation", async () => { - await browser.url(`test/pages/ColorPalettePopover.html`); + const colorPaletteButton = await browser.$("#colorPaletteBtnTest"); - const colorPaletteButton = await browser.$("#colorPaletteBtn"); + // act - open color palette popover await colorPaletteButton.click(); - const colorPalettePopover = await browser.$("[ui5-color-palette-popover]"); + + const colorPalettePopover = await browser.$("#colorPalettePopoverTest"); const colorPalette = await colorPalettePopover.shadow$("[ui5-responsive-popover]").$("[ui5-color-palette]"); + // assert assert.ok(await colorPalette.getProperty("showDefaultColor"), "Check if default color is on"); assert.ok(await colorPalette.getProperty("showRecentColors"), "Check if recent colors is on"); assert.ok(await colorPalette.getProperty("showMoreColors"), "Check if more colors is on"); - }); + }) + });