UI5 offers a couple of widgets that can expand and collapse. To do that, these controls render a button with an icon that indicates the current state, and which the user can click to toggle the state.
The standard icons that UI5 renders for the expand/collapse button are navigation arrows, which some of our users disliked. In this tip, you'll learn how you can replace them with more appropriate icons using only a few lines of CSS. No javascript code is involved.
If you want to check out this tip yourself, download the app from the
expandcollapse
directory and expose it to your webserver. You can then navigate to
index.html
to see the sample app in effect.
UI5 exandable/collapsible Controls
First, lets take a look at the standard UI5 controls.
Panel
The
sap.m.Panel
has an
expandable
property. If
true
the Panel renders a button which the user can use to hide and show the contents of the panel. A screenshot is shown below:
(This screenshot is taken from UI5's
Panel - Expand / Collapse sample)
Tree
The
sap.m.Tree
is a classical way of presenting hierarchically organized items like a folder structure. A screenshot is shown below:
(This screenshot is taken from UI5's
Tree - Basic sample)
TreeTable
The
sap.ui.table.TreeTable
is just like a regular data grid table (
sap.ui.table.Table
), but with an added functionality to hierarchically organize the rows in the table, and with the ability to expand or collapse rows according to the hierarchy. A screenshot is shown below:
(This screenshot is taken from UI5's
sap.ui.table.TreeTable
JSONTreeBinding sample)
A look at the icons
Let's take a look at the standard icons that UI5 renders for the expand/collapse button:
Proposed Icons
While I don't really have a problem with these icons, some of our users had a problem recognizing the collapse/expand functionality for Panels. We looked a bit around in
the UI5 Icon explorer and decided we'd rather use these icons instead:
Going by their name, it's a bit of a mystery to me why they weren't used by UI5 in the first place. But anway, now we have this tip to explain how you can change them.
CSS to change the icons
We prepared a separate CSS file for each of the aforementioned UI5 controls, and included them into the app via the
manifest.json
:
"resources": {
"css": [
{ "uri": "css/ui5-customization-m.Panel.css" },
{ "uri": "css/ui5-customization-m.TabContainer.css" },
{ "uri": "css/ui5-customization-m.Tree.css" },
{ "uri": "css/ui5-customization-ui.tree.TreeTable.css" }
]
}
How UI5 renders icons
Before we discuss how to apply the CSS to change the icons, it's useful to understand how UI5 icon rendering works.
In general, UI5 uses icon fonts. The UI5 framework loads a
library.css
stylesheet, which has a
@font-face
rule like this:
@font-face {
font-family: "SAP-icons";
src: url('../base/fonts/SAP-icons.woff2') format('woff2'),
url('../base/fonts/SAP-icons.woff') format('woff'),
url('../base/fonts/SAP-icons.ttf') format('truetype'),
local('SAP-icons');
font-weight: normal;
font-style: normal
}
This binds the name
SAP-icons
to the font resource, and will ensure that whenever a HTML element is assigned the
font-family: "SAP-icons"
css property, it will render whatever text it contains with glyphs from that font.
Now, when using the UI5 javascript API, you don't actually ever have to deal with these details at this level. Rather, if you ever need to assign an icon explicitly, for example, when using a
sap.ui.core.Icon
control, you can assign a custom icon uri using the sap-icon protocol, which maps more or less reasonable icon names to the glyph that depicts the desired icon. (You can read more about the sap-icon uri protocol in
the Icon topic of the SAP UI5 walkthrough)
Apart from these explicitly assigned icons, the renderer classes of various UI5 controls will write out the required HTML code for the icons that just happen to be fixed to it. Let's call these structural icons. For example, there is no property that allows you to change the icon that a
sap.m.Panel
uses for its exapand/collapse button - that's just part of how the Panel happens to be coded - it's part of its structure.
As we will see in the following sections, the
font-family
is just the underlying medium that allows the UI5 framework to render icons. The details of how a particular control renderer renders its structural icons can still vary a bit, and we'll need to figure out how a particular control renders its icons before we can change them.
How sap.ui.table.TreeTable
renders the collapse/expand icons
The
sap.ui.table.TreeTable
renderer takes a straightforward approach to rendering the collapse/expand icons. If you open one of the
standard UI5 TreeTable samples, and right click the expand/collapse icon to inspect it (for example, with the Chrome developer tools), then you might see something like this:
The
sap.ui.table.TreeTable
renderer has written a
<span>
element with a
sapUiTableTreeIcon
class:
<span
class="
sapUiTableTreeIcon
sapUiTableTreeIconNodeClosed
"
title="Expand Node"
role="button"
aria-expanded="false"
></span>
The span does not actually contain any text - rather a css
::before
pseudo class is used for that. This is also used to bind it to the
"SAP-icons"
font, using the
font-family
property - this ensures that element will render glyphs from the icon font:
.sapUiTableTreeIcon::before {
font-family: "SAP-icons";
font-size: .75rem;
color: #0854a0;
}
The actual text content that determines the icon is controlled through another rule, using another css class, which uses
the css content
property to write out the character that renders the appropriate icon from the font.
When collapsed, its:
.sapUiTableTreeIcon.sapUiTableTreeIconNodeClosed::before {
content: '\e066';
}
(You may recall that
\e066
is the character that corresponds to the
navigation-right-arrow icon.)
When expanded, its:
.sapUiTableTreeIcon.sapUiTableTreeIconNodeOpen::before {
content: '\e1e2';
}
(You may recall that
\e1e2
is the character that corresponds to the
navigation-down-arrow icon.)
This way, the
sap.ui.TreeTable
only needs to change the style class from
sapUiTableTreeIconNodeClosed
to
sapUiTableTreeIconNodeOpen
on the
<span>
, depending on the expanded/collapsted state of the row: the css magic will take care of rendering the right icon.
Changing the expand/collapse icons for the sap.ui.table.TreeTable
As we have just witnessed, the
sap.ui.table.TreeTable
uses separate classes for the collapse and expand icons. This makes it really quite simple to change the icons. We only have to write our own rules for the
sapUiTableTreeIconNodeOpen::before
and
sapUiTableTreeIconNodeClosed::before
classes to mask the default ones, and assign the proper value for the
content
property:
/**
* sap.ui.table.TreeTable: better icons for expanded
*/
.sapUiTableTreeIcon.sapUiTableTreeIconNodeOpen::before {
content: '\e1d9';
}
/**
* sap.ui.table.TreeTable: better icons for collapsed
*/
.sapUiTableTreeIcon.sapUiTableTreeIconNodeClosed::before {
content: '\e1da';
}
(You will find similar rules in the
ui5-customization-ui.tree.TreeTable.css
provided by this ui5tip)
The only thing we really need to think of when applying this stylesheet is that it is loaded after UI5 framework loads the CSS specific to the
ui.tree.TreeTable
control: if our CSS is loaded before the framework's CSS, then our rules will be masked by the framework's, and we want to do it exactly the other way around.
To ensure that the framework's CSS for the
ui.tree.TreeTable
control is loaded before our custom CSS, simply include the
sap.ui.table
library in the
data-sap-ui-libs
property of the
<script>
element you use to load UI5. (see the
index.html
for this tip):
<script
id="sap-ui-bootstrap"
src="https://openui5.hana.ondemand.com/1.87.0/resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize"
data-sap-ui-libs="sap.m, sap.ui.table"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"
data-sap-ui-resourceroots='{
"ui5tips": "./"
}'
></script>
That's it! The screenshot below shows what the TreeTable looks like in this tip's sample app:
How sap.m.Panel
renders the collaps/expand icons
Let's take a look at how the
sap.m.Panel
renders its collapse/expand icon. We can again open
UI5's own sap.m.Panel
sample and use our browser's develpoment tools to inspect the page's HTML code:
.
Just like the
sap.ui.table.TreeTable
we discussed in the previous section, the
sap.m.Panel
renders a
<span>
element for the icon, which is assigned a CSS class to mark it as the icon, and wich is bound to the icon font face:
<span
data-sap-ui-icon-content=""
class="
sapUiIcon
sapUiIconMirrorInRTL
sapMBtnCustomIcon
sapMBtnIcon
sapMBtnIconLeft"
style="font-family: 'SAP\2dicons';"
></span>
And, just like for the
sap.ui.table.TreeTable
, there is a CSS rule to select the
::before
pseudo-class, which has the
content
property to insert the appropriate character that corresponds to the glyph.
.sapUiIcon::before {
content: attr(data-sap-ui-icon-content);
speak: none;
font-weight: normal;
-webkit-font-smoothing: antialiased;
}
There are some remarkable differences too with respect to the
sap.ui.tree.TreeTable
example.
In this case, there are no separate classes corresponding to the collapsed/expanded state of the Panel. Instead, the
content
property of the
.sapUiIcon::before
pseudo-class uses the value of the elements
data-sap-ui-icon-content
attribute. It will render whatever text is in the elements
data-sap-ui-icon-content
attribute.
If you check the code for the
<span>
, you'll note the
data-sap-ui-icon-content
has been assigned some text, which is rendered as a so-called .notdef glyph, both in the developer tools and here on the page. (The
.notdef glyph is the "boxed question mark").
You can copy the text from the
data-sap-ui-icon-content
attribute in the browser tools and paste it in a hex editor, or in a javascript string to figure out what its character code is, for example:
// decimal: 57839
"".charCodeAt(0)
// hex: 0xE1EF
("".charCodeAt(0)).toString(16)
It turns out that this corresponds to UI5's
slim-arrow-down icon, which has a similar appearance to the down-arrow icon.
If you collapse the panel and inspect it again, you'll notice that the value of the
data-sap-ui-icon-content
is now
0xE1ED
, which corresponds to UI5's
slim-arrow-right icon.
Changing the expand/collapse icons for the sap.m.Panel
Now, it's clear that we cannot simply mask the existing classes in the same way we did in the
sap.ui.tree.TreeTable
case. The reason is that in this case the icon is driven directly by an attribute value, not by change of style class.
Since the icon is so clearly driven by the value of the attribute, your initial hunch might be to somehow change the value that is written out to the HTML. But this would involve rewriting or overriding the
sap.m.Panel
or its renderer, and we're not quite prepared to do that just to change the icon.
But, there is a way.
What we can do is write some rules that match the
<span>
depending on the value of the
data-sap-ui-icon-content
attribute. And if we can match a CSS selector based on the attribute value, we can simply write out a
content
property with the desired character instead. This works as long as we know what values the attribute will have, which is of course the case here, as there will only be 2 different values, corresponding to the collapsed or expanded state of the panel.
This is what it looks like in
ui5-customization-m.Panel.css
:
/*
sap.m.Panel better expanded button.
The value in the predicate for data-sap-ui-icon-content may not render correctly,
but this is decimal 57839, or 0xE1EF, which corresponds to UI5's "slim-arrow-down" icon
(https://sapui5.hana.ondemand.com/sdk/test-resources/sap/m/demokit/iconExplorer/webapp/index.html#/overview/SAP-icons/?tab=grid&icon=slim-arrow-down)
*/
div.sapMPanel.sapMPanelExpandable > div > span[data-sap-ui-icon-content=].sapUiIcon::before {
content: '\e1d9';
}
/*
sap.m.Panel better collapse button
The value in the predicate for data-sap-ui-icon-content may not render correctly,
but this is decimal 57837, or 0xE1ED, which corresponds to UI5's "slim-arrow-right" icon
(https://sapui5.hana.ondemand.com/sdk/test-resources/sap/m/demokit/iconExplorer/webapp/index.html#/overview/SAP-icons/?tab=grid&icon=slim-arrow-down)
*/
div.sapMPanel.sapMPanelExpandable > div > span[data-sap-ui-icon-content=].sapUiIcon::before {
content: '\e1da';
}
Note the
span[data-sap-ui-icon-content=].sapUiIcon::before
is the essential bit that allows us to react to a specific icon value. The selector part before is there to ensure the rule will only apply to the expand/collapse button of a Panel, and not to some random other control's icon.
And, here's what it looks like in the sample app:
How sap.m.Tree
renders the collapse/expand icons
The
sap.m.Tree
uses exactly the same mechanism to render the icons as the
sap.m.Panel
does - the character that corresponds to the appropriate icon glyph is written to a
data-sap-ui-icon-content
, and the value of the attribute is rendered against the icon font's font face. The only difference with the Panel is that the
sap.m.Tree
uses the
navigation-right-arrow and
navigation-down-arrow icons, just like the
sap.ui.table.TreeTable
did.
Apart from that, we also need to ensure the first bit of the selectors are specific to the
sap.m.Tree
, which is similar to what we did for the
sap.m.Panel
.
This is what the CSS looks like in
ui5-customization-m.Tree.css:
/**
sap.m.TreeItem : better icons for collapsed
The value in the predicate for data-sap-ui-icon-content may not render correctly,
but this is decimal 57446, or 0xE066, which corresponds to UI5's "navigation-right-arrow" icon
(https://sapui5.hana.ondemand.com/sdk/test-resources/sap/m/demokit/iconExplorer/webapp/index.html#/overview/SAP-icons/?tab=grid&icon=navigation-right-arrow)
*/
li.sapMTreeItemBase > span[data-sap-ui-icon-content=].sapMTreeItemBaseExpander.sapUiIcon::before {
content: '\e1da';
}
/**
sap.m.TreeItem : better icons for expanded
The value in the predicate for data-sap-ui-icon-content may not render correctly,
but this is decimal 57826, or 0xE1E2, which corresponds to UI5's "navigation-down-arrow" icon
(https://sapui5.hana.ondemand.com/sdk/test-resources/sap/m/demokit/iconExplorer/webapp/index.html#/overview/SAP-icons/?tab=grid&icon=navigation-down-arrow)
*/
li.sapMTreeItemBase > span[data-sap-ui-icon-content=].sapMTreeItemBaseExpander.sapUiIcon::before {
content: '\e1d9';
}
And this is what the Tree looks like in the sample app:
Finally
Did you like this tip? Do you have a better tip? Feel free to post a comment and share your approach to the same or similar problem.
Want more tips? Find other posts with the
ui5tips tag!
No comments:
Post a Comment