A new architecture for Word Online’s UX Platform

Christian Gonzalez
9 min readSep 6, 2018

--

As part of the effort to deliver refreshed visuals for Word Online, we decided to build a new UX platform for Office Online to improve the long-term engineering velocity of our team. This new UX platform would be influenced by industry best practices, even if it came at the cost of the platform being a bit more painful to integrate into our monolithic applications. We went into this project as if this were the beginning of an effort to rewrite Word Online from scratch. With this approach, we hoped to build a UX platform that was isolated, extensible, and reusable. While this approach would introduce short term pain as we had to refactor our existing application to introduce the right APIs for an isolated UX platform, we knew that this would pay off in the long run as we continue to modernize other parts of our applications.

Building a standalone component

To help us ensure that our new architecture was not influenced by the structure of the existing Office Online applications, we decided that we would build the UX platform as a standalone NPM package. This NPM package would live in a new git repository and it would have its own test application that would enable us to render parts of the UX platform in isolation. This contrasts with our existing UX platform, where things like the Ribbon can only be rendered inside an application like Word Online. The hope was that this test application would be a place where developers making UI changes could see their changes in seconds instead the minutes it takes with our current stack.

There was some initial concern here that this approach would make it more difficult for developers working on bug fixes to test their changes end to end. After all, there are some bugs in Word Online that only appear when the UX platform is integrated within the larger application. In practice, we found that this isolated test app enabled us to easily separate bugs in the UX platform from bugs in the integration of the UX platform into an Office Online application. We also invested in tooling to enable developers to be able to validate changes in the UX platform inside a larger application without deploying a new version of the NPM package. A side effect of this development approach is that we did not have Office Online specific implementation details inside the UX platform, which has enabled us to share our code with other web applications within Microsoft.

Design considerations

To fully understand the design decisions we made when building up a new UX platform, it is important to understand some of the functionality we need to support in our applications. While our initial UX platform rewrite was only targeting the Ribbon, we plan to eventually port all our UX surfaces to React and TypeScript. It was very important for us to have a forward looking mindset here to enable us to scale out to more developers as the project matured.

Responsive layout

The Single Line Ribbon has a responsive design where controls can be rendered in an overflow menu to fit in narrower browser windows. This means we need to support rendering any control type in an overflow menu. We also needed to figure out a clean way for application teams to author the contents of their Ribbons and order in which controls go into an overflow. The implementation of Ribbon scaling was actually one of the more challenging parts of this rewrite and likely merits a blog post of its own.

Ribbon Scaling

Tell Me

Another interesting aspect of our UX platform is the “Tell Me” control. This is a feature that enables users to search for a command in Word Online. The Tell Me control needs to be able to query for all controls in the Ribbon to determine what results should be presented to the user. The Tell Me control was implemented years after our legacy Script# UX platform, which resulted in some clunky mechanisms for interacting with the Ribbon to identify and invoke commands. We wanted to improve the architecture in the new UX platform to ensure we were exposing the right APIs from our UX platform to prevent tight coupling between the Ribbon and Tell Me code.

Tell Me Control

Add Ins

Office Online’s extensibility platform allows for third party developers to develop features and have their own buttons in the Ribbon. Like “Tell Me”, this functionality came to Word Online a long time after the initial implementation of the UX platform. The legacy Ribbon was originally built under the assumption that all controls could be statically defined at build time. We did not have APIs for adding or removing controls at run time, so this meant that we had to write new code for add ins to support this scenario rather than leveraging the same mechanism for constructing the first party controls in the Ribbon. It was a goal of the rewrite to consolidate the ways in which controls are added to the Ribbon.

Pickit Images is an example of a third party extension that adds a Ribbon control at run time

Synchronizing state across multiple UI surfaces

Controls that appear in the Ribbon might also appear in other UI surfaces, such as context menus, floaties, and Tell Me search results. Many of these controls also have some state associated with them. An example of this can be seen with “toggle buttons” like the “bold” button in Word Online. Whenever the user’s selection contains bold text, the control has a darker background color than it does when the selection does not contain bolded text.

Bold toggle state updating with changes in the user’s selection

This state needs to be consistent across all the UI surfaces in which the “bold” button appears. Today, each of these UI surfaces has its own “bold” button whose state needs to be kept up to date, resulting in each application having code that needs to know about all these UI surfaces. For the new UX platform, we wanted to eliminate this duplication and reduce some of this coupling between core application logic and the UX platform.

The various commanding surfaces where the bold command is available

The UX Store

One of the key design decisions we made was to introduce the concept of a “UX Store”. Each control in our UI would have an associated JavaScript object known as a “control model” that represents all the metadata about that control. This includes things like the icon, label text, and tooltip for the control. The control models would also include any state that a control may have, such as the “toggled” state of the bold button. These models would not contain details about how a control should appear, enabling us to have a single model for a control that can be shared across UI surfaces.

The control model used by all toggle buttons

The UX store would also have the definitions of the Ribbon contents for a given application. This is where we would define the controls that would appear in each Ribbon tab, as well as specifying the order in which controls would be dropped into the overflow menu as the window width decreases.

Definition for the Ribbon’s insert tab. Control models are referenced by their ID to ensure that we are using the same control model across all UI surfaces.

The UX store was implemented using Redux. We had actually written three versions of the UX store using various open source state management libraries. We wrote one version using React’s setState API, another using Redux, and another version using MobX. We were actually very happy with all three versions of the store and I think we would have been successful with any of these approaches. We decided against React setState because we did not want our UX store to be React-specific and we liked how cleanly our Redux and MobX implementations separated our state from the React components. We ended up choosing Redux because we really liked the organization of the store into actions/reducers/selectors. We also liked working with immutable data structures, since we were easily able to leverage reselect and React Pure Components to get great update performance for our UI.

App Integration

To ensure that we were creating a UX platform that was truly a standalone component, we built up the majority of the UX store well before we ever tried to integrate it into the existing Word Online application. The extraction of the UX platform into an external NPM package forced us to come up with a well-defined API for communication with the UX platform. This API needed to work across all Office Online applications, not just Word Online.

There is a very large set of interactions that need to happen between the UX platform and the application, but we can break it down into two large categories.

Communication from UX platform to the application

The first category is communication from the UX platform to the application. This includes the scenario where the user clicks on the bold button in the Ribbon and the application needs to respond to that by updating the formatting of the user’s selection.

The application is responsible for registering a function, known as the command handler, that will be invoked whenever the user executes a Ribbon command. When the user interacts with a control in the Ribbon, the command handler is invoked with an object that contains information about the command that was just invoked. This payload contains the name of the command that was invoked (e.g. “Bold”) as well as any other data associated with this control execution. For toggle buttons, this will include the current toggle state of the control. For things like the font name drop down, this will include the name of the font that the user selected. The application then uses the data in the payload to execute the requested user action within the command handler.

Data flow from UX platform to Application

Communication from the application to the UX Platform

There are also scenarios when the application needs to update some state in the UX platform. An example of this is when a user invokes a keyboard shortcut to apply bold formatting to the current selection. In this scenario, the application needs to update the “toggled” state of the bold button in the Ribbon to reflect the new state of the user’s selection in the document.

The UX platform exposes an API to enable the application to update the Ribbon in response to a user action. These APIs are mostly just exposing a subset of the Redux actions available in the UX store. The platform has APIs like ‘setButtonToggleState’ which enables the application to set the toggle state of the bold button when the user’s selection changes. These APIs would make the appropriate updates in the UX store and then trigger a rerender of the Ribbon with the React props derived from the updated UX state.

Data flow from Application to UX platform

Looking back

We took a lot of inspiration from the open source community when building up our UX platform. This was most evident in our decision to create the UX platform as a standalone NPM package. This packaging strategy ended up being a forcing function for us to implement good contracts between our application and UX platform, which we expect will make our code easier to maintain going forward. The biggest difference from our previous UX platform architecture was the creation of a UX store. This design ended up solving many of the pain points of our existing UX platform.

  • We are now able to use the same APIs for the insertion of first party controls and third party controls.
  • We have eliminated the state duplication for controls that appear in multiple UI surfaces, reducing the amount of work we will have to do as we convert the remainder of our UI surfaces to React.
  • We now have a nice set of APIs that the Tell Me control can interact with to get control metadata rather than digging into implementation details of the Ribbon.

--

--