Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
david_stocker
Advisor
Advisor
0 Kudos

This is part of a tutorial series on creating extension components for Design Studio.

Last time, we added our text to the component.  We covered the dynamic positioning of the SVG text elements very thoroughly, but we deliberately left the styling aside, as we'd promised back in Part 11a to use the styling of the text elements to examine custom component CSS styling.  In principle, we can control many, many more of the properties via CSS than we previously have, but rather then refactoring our entire gauge (and making it less user friendly to anyone who is not a skilled CSS aficionado), we will focus on the text callouts to demonstrate CSS in custom components. 

Different kinds of Styling

As a general rule, every component in Design Studio can have a custom CSS class assigned, whether that component is custom or standard.  The developer of the component does not need to do anything special.  The designer simply assigns the class to the component in the properties pane.  As long as the class has been defined in either the theme, or in the application's stylesheet, it will be applied to the component:

Any class from the theme is fair game.  E.g. the default Blue Crystal theme contains a UI5 header text css class called sapMH1Style.  Copying that css class name into the application CSS Class field changes the font size from the default 16px to 1.75rem.  Adding an application stylesheet is quite easy.  For those who have never done it before, rather then go into a long side quest here, I will link to Xavier Hacking's excellent blog post on the subject.

In addition to the classes in the theme, or custom classes in the application stylesheet, a custom extension can also access its own custom stylesheets.  This can be a handy way of defining default theming for a component, especially when its properties are not already covered in a standard theme.  Since our gauge is drawn using SVG and SVG's CSS attributes don't match up completely with HTML's CSS attributes, and custom components drawn with SVG are good candidates for such custom stylesheets.

Custom Extension Stylesheets

If we want to include custom CSS classes in our extension, we have to do two things:

  1. Define a file where we'll keep our CSS styles.  We actually already have one.  You may recall from way back in Part 1, that we created our project by forking a copy from the colored box SDK sample.  It already had a css file named component.css, located at ..//res/css/component.css. 
  2. We need to include a reference to it in contribution.xml.  In versions prior to 1.6, this was always done with the cssInclude element.  That element is now deprecated with 1.6 in favor of the new  requireJS element, introduced in 1.6.  For now, we'll stick to the old way of doing things and continue to use cssInclude.  It still works and again, we inherited a such an element when we forked the colored box.

...


<stdInclude kind="d3"/>


<jsInclude>res/js/component.js</jsInclude>


<cssInclude>res/css/component.css</cssInclude>


...


Let's delete the coloredBox class from component.css and add two classes to its place.  The two classes, measure and guide, will later be used for the measure and guide texts:

Black, boldface Helvetica font in a 1.5em font size:


.measure {


  fill: black;


  font-weight: bold;


  font-family: Helvetica;


  font-size: 1.5em;


}


Grey, Helvetica font in the 1em size:


.guide {


  fill: #808080;


  font-weight: bold;


  font-family: Helvetica;


  font-size: 1em;


}


Take Note!  We are not defining the color by a color attribute, as is normal for html and what you might expect.  Since these are SVG text elements, we have to style SVG attributes.  If you use the color attribute, nothing will happen!  Since SVG separates the border line properties from the fill, you must define them separately.  We use the fill attribute to color the fonts.  Jacob Jenkov has a good overview of the CSS style attributes of an SVG text element.

You can now start Design Studio in the debugger and try them out by entering the class names into the CSS Class field:

guidemeasure

Text Element Specific CSS Styling

What we'd really like is to ve able to assign different styles to the callouts for the guides and measure.  This is actually very simple.  We're going to introduce a new text property for each callout type.  When the SVG text elements are drawn, we will apply the text property as a class attribute. 

The New Properties in contribution.xml

We'll call our new properties measureTextCSSClass and guideTextCSSClass.


<property id="measureTextCSSClass"


  title="Measure Text Class"


  type="String"


  tooltip="CSS class of the measure text (overrides component CSS class.  Default value is 'measure')"


  group="SCNGaugeTextCallouts">


</property>




<property id="guideTextCSSClass"


  title="Guide Text Class"


  type="String"


  tooltip="CSS class of the guide text (overrides component CSS class.  Default value is 'guide')"


  group="SCNGaugeTextCallouts">


</property>


We'll give them default values.  We want the measure callout to default to using the new measure class and the guide callouts to default to the new guide class.  So we'll give them corresponding defaultValue  values.


<initialization>


  ... 


  <defaultValue property="measureTextCSSClass">measure</defaultValue>


  <defaultValue property="guideTextCSSClass">guide</defaultValue>


</initialization>


Component.js Changes

As with any property that gets used in component.js, we define a delegate variable for each property:


//Part 12 CSS


me._measureTextCSSClass = 'measure';


me._guideTextCSSClass = 'guide';


And the Getter/Setters:


//Part 12 Properties


me.measureTextCSSClass = function(value) {


  if (value === undefined) {


  return me._measureTextCSSClass;


  } else {


  me._measureTextCSSClass = value;


  return me;


  }


};


me.guideTextCSSClass = function(value) {


  if (value === undefined) {


  return me._guideTextCSSClass;


  } else {


  me._guideTextCSSClass = value;


  return me;


  }


};


//End Part 12 Properties


New we need to update text elements to include the CSS class.  If there is no element specific CSS class defined, then we'll skip adding a class attribute.  In the case of the measure callout, we'll wrap the element creation in an if/else statement, checking for me._measureTextCSSClass property.  In the case of the guide callouts, we'll do the same with the  me._guideTextCSSClass property.

For the measure:


if (me._measureTextCSSClass == ""){


  //No CSS class applied


  vis.append("text")


  .attr("transform", "translate(" + measurePosition.x+ "," + measurePosition.y+ ")")


  .text(calloutTextMeasure)


  .attr("text-anchor", measureTextPosition[0])


  .attr("dy", measureTextPosition[1]);


}


else{


  //me._measureTextCSSClass has a CSS class


  vis.append("text")


  .attr("transform", "translate(" + measurePosition.x+ "," + measurePosition.y+ ")")


  .text(calloutTextMeasure)


  .attr("text-anchor", measureTextPosition[0])


  .attr("class", me._measureTextCSSClass)


  .attr("dy", measureTextPosition[1]);


}


For the guides:


if (me._guideTextCSSClass == ""){


  //Empty CSS Class version


  //Start Text


  vis.append("text")


  .attr("transform", "translate(" + guidePositionStart.x + "," + guidePositionStart.y + ")")


  .text(calloutTextStart)


  .attr("text-anchor", guideTextPositionStart[0])


  //.attr("dominant-baseline", guideTextPositionStart[1]);


  .attr("dy", guideTextPositionStart[1]);


  //End Text


  vis.append("text")


  .attr("transform", "translate(" + guidePositionEnd.x + "," + guidePositionEnd.y + ")")


  .text(calloutTextEnd)


  .attr("text-anchor", guideTextPositionEnd[0])


  //.attr("dominant-baseline", guideTextPositionEnd[1]);


  .attr("dy", guideTextPositionEnd[1]);


}


else{


  //Start Text


  vis.append("text")


  .attr("transform", "translate(" + guidePositionStart.x + "," + guidePositionStart.y + ")")


  .text(calloutTextStart)


  .attr("text-anchor", guideTextPositionStart[0])


  .attr("class", me._guideTextCSSClass)


  .attr("dy", guideTextPositionStart[1]);


  //End Text


  vis.append("text")


  .attr("transform", "translate(" + guidePositionEnd.x + "," + guidePositionEnd.y + ")")


  .text(calloutTextEnd)


  .attr("text-anchor", guideTextPositionEnd[0])


  .attr("class", me._guideTextCSSClass)


  .attr("dy", guideTextPositionEnd[1]);


}


If you now start Design Studio in debug mode, you should have the options to set the individual CSS classes of the measure and guide callouts.  Your order of appearance within the Text callout group might be different.  Order of appearance within a group is determined by the order of appearance within the component.xml file.  In my example, I'd inserted the measure callout class after the other measure properties, but before the guide ones. 

The Witches Brew: AKA the CSS Attribute Order of Precedence

At this point, there are several CSS classes that can affect both the component as a whole and the callouts.  Some are in by default and others must be chosen by the designer.  Let's take a tour of them:

sapUiBody - This class is assigned at the application level and inherited by all child elements.  As you can see below, it is quite simple, setting a black, 16px font, defaulting to one of the common serif fonts and falling back to sans-serif if Aral and Helvetica are not available in the browser.


.sapUiBody{


  background-color:#f2f2f2;


  color:#000000;


  font-family:Arial,Helvetica,sans-serif;


  font-size:16px;


  -webkit-tap-highlight-color:transparent


}


zenControl - This class is assigned to the component's container div when created.  It does not actually do anything and is used by Design Studio for housekeeping.  CSS classes make a handy tag for selecting elements.  Note that this class assigns a color attribute, which is ignored by SVG.  So the gauge and all of its elements will ignore the slate gray color setting and default to black if no fill attributes are assigned.

Designer defined Component CSS class - If the designer assigns a component level CSS class, that is also assigned at the container div level.

Designer defined  SVG Text Clement CSS class - If the designer assigns a text element level css class - using the properties that we just added - then that class is assigned to the SVG text element.

Always keep in mind that the attributes of the most specific class always override those of  less specific ones.  So if I apply a font size of 40px  and the fill attribute to green in the component level class, but leave the font-family setting the same, I'll get a green, 40px Aral font when displayed. If I then set the fill to red in the SVG text element CSS class - while leaving the other settings untouched - will result in a red, 40px Aral font.

As usual, there is a public project on Github, with the eclipse project and the test application (in exported zip form).  Below is a short demo video of the results.