Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui5-icon): introduce interactive property #1592

Merged
merged 3 commits into from
May 13, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/main/src/Icon.hbs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
<svg
class="ui5-icon-root"
tabindex="{{tabIndex}}"
dir="{{dir}}"
viewBox="0 0 512 512"
role="img"
focusable="false"
preserveAspectRatio="xMidYMid meet"
aria-label="{{accessibleNameText}}"
xmlns="http://www.w3.org/2000/svg"
@focusin={{_onfocusin}}
@focusout={{_onfocusout}}
@keydown={{_onkeydown}}
@keyup={{_onkeyup}}
@click={{_onclick}}
>
{{#if hasIconTooltip}}
<title id="{{_id}}-tooltip">{{accessibleNameText}}</title>
Expand Down
61 changes: 61 additions & 0 deletions packages/main/src/Icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getRTL } from "@ui5/webcomponents-base/dist/config/RTL.js";
import { getIconData, getIconDataSync } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
import createStyleInHead from "@ui5/webcomponents-base/dist/util/createStyleInHead.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { isSpace, isEnter } from "@ui5/webcomponents-base/dist/Keys.js";
import IconTemplate from "./generated/templates/IconTemplate.lit.js";

// Styles
Expand All @@ -17,6 +18,17 @@ const ICON_NOT_FOUND = "ICON_NOT_FOUND";
const metadata = {
tag: "ui5-icon",
properties: /** @lends sap.ui.webcomponents.main.Icon.prototype */ {
/**
* Defines if the icon is interactive (focusable and pressable)
* @type {boolean}
* @defaultvalue false
* @public
* @since 1.0.0-rc.8
*/
interactive: {
type: Boolean,
},

/**
* Defines the unique identifier (icon name) of each <code>ui5-icon</code>.
* <br><br>
Expand Down Expand Up @@ -77,6 +89,13 @@ const metadata = {
noAttribute: true,
},

/**
* @private
*/
focused: {
type: Boolean,
},

/**
* @private
*/
Expand All @@ -85,6 +104,14 @@ const metadata = {
},
},
events: {
/**
fifoosid marked this conversation as resolved.
Show resolved Hide resolved
* Fired on mouseup, space and enter
* @private
* @since 1.0.0-rc.8
*/
click: {

},
},
};

Expand Down Expand Up @@ -137,6 +164,40 @@ class Icon extends UI5Element {
await fetchI18nBundle("@ui5/webcomponents");
}

_onfocusin(event) {
if (this.interactive) {
this.focused = true;
}
}

_onfocusout(event) {
this.focused = false;
}

_onkeydown(event) {
if (this.interactive && isEnter(event)) {
this.fireEvent("click");
}
}

_onkeyup(event) {
if (this.interactive && isSpace(event)) {
this.fireEvent("click");
}
}

_onclick(event) {
event.preventDefault();
fifoosid marked this conversation as resolved.
Show resolved Hide resolved
if (this.interactive) {
// Prevent the native event and fire custom event because otherwise the noConfict event won't be thrown
this.fireEvent("click");
}
}

get tabIndex() {
return this.interactive ? "0" : "-1";
}

static createGlobalStyle() {
if (!window.ShadyDOM) {
return;
Expand Down
5 changes: 5 additions & 0 deletions packages/main/src/themes/Icon.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
outline: none;
}

:host([interactive][focused]) .ui5-icon-root {
outline: 1px dotted var(--sapContent_FocusColor);
}

:host(:not([dir="ltr"])) .ui5-icon-root[dir=rtl] {
transform: scale(-1, -1);
transform-origin: center;
Expand All @@ -27,4 +31,5 @@
display:flex;
transform: scale(1, -1);
transform-origin: center;
outline: none;
}
24 changes: 23 additions & 1 deletion packages/main/test/pages/Icon.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,29 @@
></ui5-icon>
</ui5-datepicker>

<br/><br/>
<br/><br/>

<ui5-icon id="interactive-icon" name="add-equipment" class="icon-blue icon-medium" interactive></ui5-icon>
<ui5-icon id="non-interactive-icon" name="add-equipment" class="icon-blue icon-medium"></ui5-icon>
<ui5-input id="click-event" value="0"></ui5-input>

<br/>
<br/>

<script>
var icon = document.getElementById("interactive-icon"),
nonInteractiveIcon = document.getElementById("non-interactive-icon"),
input = document.getElementById("click-event"),
inputValue = 0;

icon.addEventListener("ui5-click", function() {
input.value = ++inputValue;
});

nonInteractiveIcon.addEventListener("ui5-click", function() {
input.value = ++inputValue;
});
</script>

<script type="module">
(async () => {
Expand Down
40 changes: 39 additions & 1 deletion packages/main/test/specs/Icon.spec.js
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
const assert = require("chai").assert;
const assert = require("chai").assert;

describe("Icon general interaction", () => {
browser.url("http://localhost:8080/test-resources/pages/Icon.html");

it("Tests icon rendering", () => {
const iconRoot = browser.$("#interactive-icon").shadow$("ui5-icon-root");

assert.ok(iconRoot, "Icon is rendered");
});

it("Tests if clicked event is thrown for interactive icons", () => {
const iconRoot = browser.$("#interactive-icon").shadow$(".ui5-icon-root");
const input = browser.$("#click-event");

iconRoot.click();
assert.strictEqual(input.getAttribute("value"), "1", "Mouse click throws event");

iconRoot.keys("Enter");
assert.strictEqual(input.getAttribute("value"), "2", "Enter throws event");

iconRoot.keys("Space");
assert.strictEqual(input.getAttribute("value"), "3", "Space throws event");
});

it("Tests if clicked event is not thrown for non interactive icons", () => {
const iconRoot = browser.$("#non-interactive-icon");
const input = browser.$("#click-event");

iconRoot.click();
assert.strictEqual(input.getAttribute("value"), "3", "Mouse click throws event");

iconRoot.keys("Enter");
assert.strictEqual(input.getAttribute("value"), "3", "Enter throws event");

iconRoot.keys("Space");
assert.strictEqual(input.getAttribute("value"), "3", "Space throws event");
});
});