February 18th, 2018

Microsoft Teams App Studio’s Control Library

If you’ve tried out our new Microsoft Teams App Studio you’ve probably noticed the “Control library” tab. We developed this library based on feedback from our developer community to simplify the design and development process. Each time we created a new tab we found ourselves copying the same CSS code repeatedly — as you can imagine there were several issues:

  • Every time the Microsoft Teams designers made a visual change, we needed to update the CSS for every tab
  • Teams client supports three different themes, light (the default), dark, and high-contrast; all of which need to be updated
  • The keyboard navigation behavior across controls becomes inconsistent in different tabs if they are implemented differently

To resolve these issues, we built a library for common UI elements used in Teams. Using this library will enable your tab to stay consistent with the Teams look and feel.

While the full Teams control library uses the React UI framework, it’s built so that it’s not tied to a specific UI framework. In fact, we have four different npm packages:

They are all open source; you can use msteams-ui-styles-core and msteams-ui-icons-core without React. Assuming you already use React or are willing to try, here’s what to do:

  1. Create a react app. Skip if you have an existing one:
create-react-app hello-teams && cd hello-teams
  1. Install control library and its peer dependency ‘typestyle’
npm install --save typestyle && npm install --save msteams-ui-components-react

Optionally, install msteams-ui-icons-react if you want to use Teams icons.

npm install --save msteams-ui-icons-react
  1. Edit `src/App.js` file, replace its content with following code:
import React, { Component } from ‘react’;
import { TeamsComponentContext, ThemeStyle, PrimaryButton } from ‘msteams-ui-components-react’

class App extends Component {
   render() {
      // Sets up the top-level context for the library. It accepts global
      // configurations, e.g. font size and theme. fontSize is your page’s
      // default font size. We made it a parameter so that you could use this
      // library with CSS frameworks such as Bootstrap. Some CSS frameworks
      // set the default font size for the page; retrieve it and use it 
      // instead of {16} in the block. 
      // This library uses the power of CSS to most of the work for you.
      // Instead of passing themes as a parameter to every UI component, 
      // we set it on a parent HTML element. All HTML elements nested within 
      // that parent will inherit these properties. 
      return (
	<TeamsComponentContext
  	    fontSize={16}
	    theme={ThemeStyle.Light}
        />
      );
   }
}

export default App;
  1. Run
npm run start
  1. When you navigate to http://localhost:3000, you should see your button.

Dynamically Handling Theme Change

You need to handle themes in two scenarios: one when the tab is initially loaded, and the other when a user changes the theme after the tab is already loaded. The theme is included in a tab’s Context, which can be retrieved before the tab is loaded via URL placeholder values, or at any time by using micros…from the Microsoft Teams JavaScript client SDK. How the current theme is retrieved and how to respond to theme changes is discussed here: Get context for your Microsoft Teams tab.

The sample code below shows how this is done.

componentWillMount() {
	// If you are deploying your site as a MS Teams static or configurable tab,
	// you should add “?theme={theme}” to your tabs URL in the manifest.
	// That way you will get the current theme before it’s loaded; getContext()
	// is called only after the tab is loaded, which will cause the tab to flash
	// if the current theme is different than the default.
	this.updateTheme(this.getQueryVariable('theme'));
	this.setState({
	   fontSize: this.pageFontSize(),
	});

	// If you are not using the MS Teams Javascript SDK, you can remove this entire
	// if block, but if you want theme changes in the MS Teams client to propagate
	// to the tab, leave it here.
	if (this.inTeams()) {
	   microsoftTeams.initialize();
	   microsoftTeams.registerOnThemeChangeHandler(this.updateTheme);
	}
   }

Connect Your Own Component to the TeamsComponentContext

Sometimes you want to write your own CSS code and still use some colors defined by Teams. Sometimes you want to write your own CSS code but still respond to theme change. We offer a way to hook your component with TeamsComponentContext.

Once again, edit your ‘src/App.js’ file and replace its content with following code:

import React, { Component } from ‘react’;
import { TeamsComponentContext, ThemeStyle, ConnectedComponent } from ‘msteams-ui-components-react’

class App extends Component {
   render() {
      return (
	<TeamsComponentContext
	  fontSize={16}
	  theme={ThemeStyle.HighContrast}>
	  <MyComponent />
	</TeamsComponentContext>
       );
     }
 }
 
class MyComponent extends Component {
   render() {
      return (
	<ConnectedComponent render={(props) => {
	  const context = props.context;

	  switch (context.style) {
	    case ThemeStyle.Dark:
	      return <div style={{ color: context.colors.dark.brand00 }}>Dark theme!</div>;
	    case ThemeStyle.HighContrast:
	      return <div style={{ color: context.colors.highContrast.black }}>High Contrast theme!</div>;
	    case ThemeStyle.Light:
	      return <div style={{ color: context.colors.light.brand00 }}>Light theme!</div>;
	  }
	}} />
      );
   }
}

export default App;

In the above code, we define a new component called MyComponent. We then use a special component offered by the control library called ConnectedComponentConnectedComponent has a property called render which takes a function as parameter. At render time, this function will be called with the appropriate context for your tab. The context includes the theme that the page is being rendered in as well as the global color object that you can use to apply Teams colors to your tab. As you can see in the switch statement above, we choose the theme appropriate div to render.

To change themes, we need to pass the root-level TeamsComponentContext a different theme. When a theme changes, all the child elements wrapped in ConnectedComponent will be re-rendered. See previous section “Dynamically Handle Theme Change.”

Alternative Way to Connect Your Component to TeamsComponentContext:

If you’re familiar with Redux, you may prefer the following way of connecting to TeamsComponentContext:

import React, { Component } from ‘react’;
import { TeamsComponentContext, ThemeStyle, connectTeamsComponent } from ‘msteams-ui-components-react’

class App extends Component {
   render() {
     return (
	<TeamsComponentContext
	fontSize={16}
	theme={ThemeStyle.HighContrast}>
	<MyComponent />
	</TeamsComponentContext>
      );
   }
}

class MyComponentInner extends Component {
   render() {
      const context = this.props.context;
      switch (context.style) {
	case ThemeStyle.Dark:
	   return <div style={{ color: context.colors.dark.brand00 }}>Dark theme!</div>;
	case ThemeStyle.HighContrast:
	   return <div style={{ color: context.colors.highContrast.black }}>High Contrast theme!</div>;
	case ThemeStyle.Light:
	   return <div style={{ color: context.colors.light.brand00 }}>Light theme!</div>;
      }
   }
 }

const MyComponent = connectTeamsComponent(MyComponentInner);

export default App;

In this method, instead of using ConnectedComponent, we use the connectTeamsComponent function. The connectTeamsComponent function takes your current component and returns a new component with the context object injected.

Next Steps

Head to Teams App Studio and check out all the elements we offer and sample code of how to use them. Don’t forget to explore them in different themes!