At Lou we build out tools to help customers onboard users onto their platform in order to provide a better user experience and increase user retention. One of these tools is the Lou Builder which is a Chrome browser extension that allows the customer to build an onboarding experience right on top of the website with little to no code at all. During my time at Lou I worked mainly on features for the Builder extension and also made improvements to our other services such as the Assistant Script, Dashboard, Landing Page, and API. Some of my most notable contributions included the Builder Redesign, Widgets, and Workflow Screen Size.
Over the 2+ years I spent at Lou, the team and I worked to plan and execute features that would address our current clients' needs but also help attract new customers. Our product adopted a tiered payment model ranging from free to enterprise to broaden the types of clients that we can service. With this, the free tier provides access to a majority of our features with limitations on their use which can be accessed through a paid tier. Along with the outreach done by our buisness development team, this model proved successful as our company"s Monthly Recurring Revenue (MRR) incease by a factor greater than 50x during this period. We attribute this to a collaboration of our team's actions from our client outreach to our service approach to new features. When a client uses Lou, they can expect a consistent flow of new features some of which have been Goals, A / B Testing, and many new Experience step types. Along with this our outreach team works in tandem with all of our clients to address any needs and improvements that can be made to the product to provide the best user experience.
For my first project I worked on creating "Turn Key" segments (which allows customers to identify and group users) so that using this feature is available out of the box. For this I worked on creating front-end components to display these items on both the dashboard and builder extension. My contributions to this feature focused mainly on updating the styles and served as a nice first project to get my development environment up and ready.
This project I worked on updating the landing page to provide pixel perfect and responsive implementations of the provided mock-ups. For this I utilized CSS grid properties to handle each of the sections and divide them into columns and rows. With appropriate media queries I updated each page to either scale each section or organize the sections into a single column to fit on mobile and tablet devices.
Later on the Pricing Page was updated to provide a more interactive experience to the client as a slider was introduced to display how pricing would change according to the amount of MAUs (Monthly Active Users). This along with small changes to the Home Page were implemented to achieve a more modern look.
This feature is specific to the Builder where it made creating onboarding Experiences for new a client easier by selecting from a fitting template. From this, the created experience can be edited or added upon to better fit the customer's website. This is an improvement on the previous implementation of a blank Experience as it removes the friction encounter when creating the first Experience.
Templates were later on updated to have its own dedicated page within the sidebar as the variety of different Experience types and thus Templates had grown so large. Having this page specific to Experience Templates provided room to expand its functionality such that a Template option can preview its first step when hovered upon. This allows the client to see what would be created before selecting a specific option.
The Builder extension provides a fairly intuitive user interface but its current implementation limits the potential features that can be added on top of it. In this state before its redesign, the extension provides a WYSIWYG (What You See Is What You Get) editor for editing content within an experience restricted inside an <iframe>
, sufficient for its existing scope but limiting in potential features. Some of these difficulties include dealing with the height, width, positioning, and user interactions of the experience steps along with that of the <iframe>
parent. With this in mind, we decided to move away from the contraints of an <iframe>
and instead utilize the Shadow DOM to keep the experience step components isolated away from the host's website, however these also came with its own set of challenges.
In addition to these structural refactoring, we also wanted to enhance the user experience by providing a set of customizable features such as colors, fonts, and themes. For this we needed to upgrade our implementation of our rich text editor so that these complex features can be added in. Along with this other small touches such as hover previews for created experiences were implemented to provide a more fluid user experience. As shown in the mock-ups below, the build flow of an experience was redesigned to make use of these new features and aid in creating a new experience.
This ended up being one of the larger projects that spanned over the course of a couple of months and completely changed the Lou Builder Chrome extension to provide a nicer and more comprehensive user experience.
[email protected]
The first part of this project involved updating our implementation of the WYSIWYG (What You See Is What You Get) editor to utilize further customizable features such as color and fonts. For this we have been using a third party wrapper of the package draft-js
called megadraft
which has been sufficient for us at this point. However with the goals features of this project, we quickly ran into the limitations of megadraft
and opted to directly usedraft-js
itself. This was feasable since the content is stored the same draft-js
format in both of the packages and did not need to worry about migrating existing data to a new type of format.
Since draft-js
replaced megadraft
as our core dependency for our rich text editor, the included out-of-the-box features such as the basic controls that we previously used were no longer available. This meant recreating the basic controls (i.e.Bold, Italics, Underline, etc.) along with new controls for font family and font color. For this we created our own package to house our components created using draft-js
which we named @lou-assistant/draft-components
.
In order to provide a font family that is as close or exactly that of the one used on the host site, we use an API provided by Google Fonts to provide access to hundreds of free fonts. To do this we follow the API documentation to use the npm package, webfontloader
to load in the appropriate files to initialize the desired font family. As for finding the perfect font, a search dropdown component was created to allow the user to type in a desired fornt and peruse through a list of matching results. This search component was integrated into the toolbar alongside the custom font functionality for the WYSIWYG editor.
For the font and highlight colors we used the npm package react-color
as our color picker component to input or select a color from the host website. At this point we ran into incompatibility issues with the Shadow DOM and some of our core dependencies such including react-color
and draft-js
. Since moving away from using the Shadow DOM was not an option, the packages were forked and support for use within the Shadow DOM was added in. This was a major hurdle to overcome and once this was done a small amount of polishing and bug fixing was all that was necessary to implement this new editor into the builder. With these components published as a package, we were able to easily use this in our other products such as the Dashboard website and Assistant script.
When building experiences, our users typically end up using the same styles that match their company's and it can be tedious to enter the same configurations each time. The Themes feature will solve this problem which allows for the commonly used company presets to be saved and used when creating other experiences with the Lou Builder. The configurations for these company style presets can be accessed mainly through the Dashboard page where when saved can be utilized through the Lou Builder.
On the server we refer to experiences as workflows
and record the changes made whenever saved as revisions
. This is useful for archival purposes and for our own analytics when considering which new projects would have greatest impact. For this admin feature we want to provide a page on the KPI Dashboard to search and view workflows
along with their respective revisions
. On this feature I spent some time working on the API to build a paginated route to retrieve the appropriate revisions
. This route is then called from the Dashboard and with some tweaks to the exisiting components, the experience can be viewed at the time of the revision
. This feature gave me a taste of building out improvements to the back-end and integrating these changes on front-end applications such as the Builder and Dashboard.
Another feature that clients have asked for is the ability to create goals associated with experiences or when a specific event is created. To achieve this a Django app for handling events was created so that a specific goal can be marked as completed if the event with the corresponding name exists. With this the client is able to get insight into user segments and how they respond to the created experiences.
The initial iteration of this goal form is built inside the existing sidebar page for building out experiences. For this Goals have their own dedicated tab in which the Title, Description, and Event Name can be attributed to it. Along with this the tab allows for a created Goal to be associated with the experience but the ability to create and attach an event is notably missing here. This is done intentionally as at this point the events can only be created through the Lou.track()
function in our SDK provided when the user installs our script. This limited the accessibility of this feature to those familiar with our SDK but was enough to get the feature published.
For these goals to be useful, the client needs to access the accrued information and metrics from the users that complete these goals. For this the experience page was updated to display the associated metrics alongside the other general analytics.
The Goals feature in its existing state allows for the collection of metrics associated with the attached experience. The issue it runs into is the accessiblity of this feature is quite limited due to its exclusive use through the Lou SDK's Lou.track()
function. The next update to this feature would move the form to its own page and extend its uses to 4 different Goal types: Click Action, Hover Action, Page Load Action, and Custom Action.
The Click and Hover Action Goal types allows for the client to select an eleement for the website and use that to send an event if it is clicked or hovered upon respectively. The Page Load Action Goal type will create an event to complete a goal when the URL for a specified page is loaded. Lastly the Custom Action Goal allows for the client to use the existing way of creating events through the Lou SDK's Lou.track()
function. Each of these Goals will need an Event Name provided to it that will be used to determine if the Goal is completed. In addition a more readable title should be provided as this is displayed when selecting a Goal to associate with an Experience.
While creating the Goal, many of the fields are autogenerated for the client's benefit all of which can be edited. The Event Name uses a UUID as to create a unique identifier different from any created before. Once the Goal is created it is automatically selected to be used with the experience the user is currently working on.
With the enhancements to the Goal feature, it is made more accessible and convenient to use while creating an Experience making it appropriate to classify as "Code Free".
In order to provide a more personalized Experience, the option to include more information about the client is available through Profile Headers. This Profile Header will allow for an image, first name, and company name to be displayed on each step of the Experience to the user. The aim for this feature is to give the end user of this Experience to feel like confident and comfortable by the guidance provided as there is now a face and name to attach to it.
The first issue that needed to be addressed here is updating the Dashboard so that the client is able to input the appropriate details such as the client's name and image. For this a route was created to appropriately handle image uploading and attach this uploaded image to the profile data for the company member. Once the image was uploaded to our AWS Bucket, the corresponding URL is then attached to the profile data and utilized as the image for the Profile Header.
On the Lou Builder this feature is accessible through a simple toggle where if enabled, the Profile Header is place at the top of every Experience step.
So far the experiences have consisted of either Modal or Tooltip like steps of which help guide the user through the features available in the client's platform. These have been sufficient for most use cases but some situations would benefit from a Banner like Step in order to immediately get the user's attention. For this Banners builds on top of the existing structure for a workflow
with the addition of special placement to the top and bottom of the page along with alignment of buttons to the left or right side of the Banner.
Creating a Banner Experience is virtually the same as any other Experience through the Lou Builder with a couple of different fields in the toolbar. The Banner Step can be used in combination with other Step types such as Modals or Tooltips as to provide an additional tool for creating experiences in the client's toolkit.
A / B Testing allows for clients to see if the experience that was created has an impact on their users. For this feature it utilizes the previous Goals feature as metrics regarding completetion rate can be use to measure the effectiveness of an Experience.
To display the metrics of the A / B testing feature, another card specific to this is included below the other existing metrics for an Experience in the Dashboard. Here the card is divided into two sections of Group A and Group B where one is shown the Experience and the other is not respectively. The metrics then show the users' response with or without being shown the Experience with the percentage metric shown inside the Donut Pie Chart. The results of each group can be compared to each other differences between the two can be interpreted accordingly.
As for enabling this feature, it can be done inside the Lou Builder and is as simple as toggling a switch.
Along with general workflow experiences such as Tours and Announcements, we want to provide our customers with a more persistant experience feature between page visits. After completing a Tour or Announcement, users would benefit from additional guidance on exploring platform features that might not be extremely obvious. For this we built out a new creatable experience type alongside workflows
, called widgets
. With widgets
, experiences can be created to address a number of situations ranging from simple messages to checklists which indicates progress through connected experiences.
This projects revamps the process for creating an experience through the Lou Builder by further broadening the customizability of an experience. The most prominent of these changes is the new Blocks structure of a widget
, which allows customizability in the type (i.e. Checklist Item, Progress, Rich Text, etc.) and ordering of the content within. Other additions include the ability to connect workflows to checklist items, persisting experience state, new toolbar components for the Builder, and updated Sidebar pages for the Lou Builder. The ambitious scope of this project made it one of the largest I have worked on to this date as it was essentially a whole new product with prior knowledge from developing workflows
. Due to this, the project was done on and off over the course of multiple quarters as to address some of the immediate concerns of our existing customers. In the end, we were able to complete this project and push out a new product that our customers enjoy using.
Placement
A widget
is customizable such that it can be placed anywhere on the site but for ease of use we provide a 3 x 3 placement
grid inside the Lou Builder toolbar for quick access to placements
ranging from the top left corner to the bottom right corner. In conjunction with this, inputs for providing values for top
, left
, right
, bottom
positions
can be used to further specify where the widget
should be put. Because widgets
can use precision greater than that of placements
, the position
values are stored and primarily used. It is only when the widget
experiences are edited that these positions
are also converted into placement
values to be used within the Lou Builder's toolbar 3 x 3 grid.
As mentioned before, the area of the screen in which the widget
is placed is primarily determined by the position
values. The position
values (i.e. string
values for css properties such as top
, left
, right
, bottom
) needed to be converted into their appropriate placement
values. These placement
values would represent a 3 x 3 grid and as such would resemble something like the following.
+-------------+---------------+--------------+ | Top Left | Top Center | Top Right | +-------------+---------------+--------------+ | Middle Left | Middle Center | Middle Right | +-------------+---------------+--------------+ | Bottom Left | Bottom Center | Bottom Right | +-------------+---------------+--------------+
Placement
values in this grid are determined by a combination of position
values that match either 0px
or 50%
(Top
, Left
, Bottom
, Right
, or Middle
, Center
respectively). Since this 3 x 3 grid uses a combination of these placement
values, we need to be able to know if the position
values are a valid combination. (i.e. A position
where the css values for top
is 0px
and left
is 50%
would correspond to a placement
of TopCenter
). One possible approach to checking these combinations could be to extensively check every possible combination in order to provide the associated placement
. Another approach would be to leverage the Bitwise shift operators in conjunction with TypeScript enums
to check the individual values and determine if they add up into a valid combination.
/**
* @description
* Placments declared using bit shifting for complex conditionals.
* +------------------+--------------------+-------------------+---+
* | 8 | 16 | 32 | + |
* +------------------+--------------------+-------------------+---+
* | Top Left (9) | Top Center (17) | Top Right (33) | 1 |
* +------------------+--------------------+-------------------+---+
* | Middle Left (10) | Middle Center (18) | Middle Right (34) | 2 |
* +------------------+--------------------+-------------------+---+
* | Bottom Left (12) | Bottom Center (20) | Bottom Right (36) | 4 |
* +------------------+--------------------+-------------------+---+
*
* @example Determining placement from dimensional values.
* const dimensionsToPlacement = (dimensions): Placement => {
* // Placement will be returned unchanged if no conditionals are met.
* let placement = Placement.Custom; // 000000 = 0
*
* // Vertical
* if (dimensions.top === '0px') {
* placement += Placement.Top; // 000001 = 1
* } else if (dimensions.top === '50%') {
* placement += Placement.Middle; // 000010 = 2
* } else if (dimensions.bottom === '0px') {
* placement += Placement.Bottom; // 000100 = 4
* }
*
* // Horizontal
* if (dimensions.left === '0px') {
* placement += Placement.Left; // 001000 = 8
* } else if (dimensions.left === '50%') {
* placement += Placement.Center; // 010000 = 16
* } else if (dimensions.right === '0px') {
* placement += Placement.Right; // 100000 = 32
* }
*
* return placement; // Placement.TopLeft === 1 + 8
* }
*/
export enum Placement {
Custom = 0, // 000000 = 0
Top = 1 << 0, // 000001 = 1
Middle = 1 << 1, // 000010 = 2
Bottom = 1 << 2, // 000100 = 4
Left = 1 << 3, // 001000 = 8
Center = 1 << 4, // 010000 = 16
Right = 1 << 5, // 100000 = 32
TopLeft = Top | Left, // 001001 = 9 (1 + 8 = 9)
TopCenter = Top | Center, // 010001 = 17 (1 + 16 = 17)
TopRight = Top | Right, // 100001 = 33 (1 + 32 = 33)
MiddleLeft = Middle | Left, // 001010 = 10 (2 + 8 = 10)
MiddleCenter = Middle | Center, // 010010 = 18 (2 + 16 = 18)
MiddleRight = Middle | Right, // 100010 = 34 (2 + 32 = 34)
BottomLeft = Bottom | Left, // 001100 = 12 (4 + 8 = 12)
BottomCenter = Bottom | Center, // 010100 = 20 (4 + 16 = 20)
BottomRight = Bottom | Right, // 100100 = 36 (4 + 32 = 36)
}
In short, each of these enums
can be added to another matching enum
and the sum of these two would result in a separate but valid enum
. This then can be used to populate the 3 x 3 grid within the toolbar to indicate that the following positions
match the appropriate placement
. The converse of this is applicable as selecting a placement
will also provide the appropriate positions
. The toolbar will also place itself appropriately alongside the widget
container so that it visible regardless of placement
.
I credit dyc3 for introducing me to applications of bitwise operators while I watching one of his code review videos. This proved useful and applicable to this project as it solves a similar problem of dealing with complex states such as placements
in the 3 x 3 grid.
[email protected]
For workflow
related experiences only one draft-js
editor was utilized at a given time. With this assumption, much of the functions and components in [email protected]
were constructed with far too much overhead to be practical if used multiple times within a parent element. The outline of this new widget
features includes the blocks
structure where multiple instances of the draft-js
editor would need to be used alongside each other in blocks
such as Checklist Item or Content. For this new feature, much of the content inside of draft-components
would be refactored and redesigned to make it easier to use multiple instances of the draft-js
editor alongside each other.
In its current state, [email protected]
heavily utilizes React's Context
API for its state management. This is used primarily to dispatch actions and denote the current EditorState
through the toolbar controls. This follows a design similar to the action
, reducer
, store
seen in a state management package called Redux
. This provided some needed structure when exploring some of the features and API in draft-js
, however added significant overhead to each editor instance.
The heaviest portion of this overhead is in the <Provider />
component required to wrap each draft-js
editor component separately. Because of this requirement, it would be difficult to intialize / remove multiple <Provider />
components depending on the amount of applicable blocks
are used. A better approach would be to move the state management for EditorState
outside the package that way it only needs to be initialized when needed. This would also allow for multiple EditorState
instances to be managed in a parent component making it extenisble to the blocks
structure for a widget
.
One way of looking at this redesign to draft-components
would be that it now provides the tools to assemble an editor instead of providing it out of the box. This has its drawbacks where more assembly is required in some cases such as the Lou Builder, but provides a simplier experience in most other cases. With this, draft-components
provides a simple component export for <Editor />
, the state of which can be controlled through props
instead of a <Provider />
parent component. This new design replaces the previous but since the relegated design is still in use with workflow
experiences those exports have been moved into a legacy
folder to be addressed at a future date.
Blocks
The blocks
structure that is utilized with widgets
provides a new level of customizability and personalization to experiences. Customers have access to greater degree of freedom when building experiences in that they are able to select and arrange the appropriate blocks
in any desired configuration. The blocks
provide the basic content editor along with other specialized functions such as progress, checkboxes, and dividers; enabling additional interactive functionality. These new features allows for our customers to build out experiences as close to their ideal design while introducing new possible ways to interact with the user.
Blocks
can be added directly from the toolbar through a dedicated page displaying the possible options and reached limits for types such as Progress block
(Only one Progress block is allowed per widget
since it does not make sense to allow multiple). In the example below a Divider block is added and rearranged to be moved below the Progress block
and then removed. This outlines a small snippet of the user flow and freedom provided when building a widget
such that quickly trying out new ideas is quite accessible.
Blocks
- Content The Content block
is a newer iteration of the core rich text editor used with workflow
experiences. This version of the editor utilizes the changes in [email protected]
to provide a more responsive and faster building experience. Along with this, the popular Google Fonts search and text color features have been ported over so that no compromises are made between the two versions. For its use in widgets
, this block
acts as the basic building block for adding text and images viewable to the user.
Blocks
- Progress The Progress block
is a new addition as it provides a dynamic indicator to the user denoting the progress through checklist items as they are completed. In this block
basic information such as the amount of completed checklist items and color of the progress bar can be customized to match that of the customer's designs.
In order to display the completion of Checklist Items, the state of this needs to kept and updated somewhere. The Progress block
component is kept simple by remaining a stateless component as it only visually indicates what is fed to it through props
. The completion state is kept in the parent component and is managed to stay in sync with the data provided by the server, local storage, and user interactions. This block
will update in realtime as Checklist Items are marked as completed or if Checklist Item blocks
are added or removed. There is a limit of one Progress block
per widget
as having multiple Progress blocks
would serve no additional purpose.
Blocks
- Checklist Item The Checklist Item block
is the flagship feature for widgets
as it provides the same customizable editor used for the Content block
alongside the dynamic state properites of the Progress block
. A Checklist Item helps guide the user through the process of onboarding by providing an indicator of completed experiences or tasks alongside the remaining ones.
Similar to how the Content block
is able to have its text styled through the draft-js
editor, the Checklist Item block
has all of that same functionality. This block
in addition to content allows for the customer to change the color of the checkmark to a more appropriate one to better fit with the company's theme. This change can then be seen when previewed as complete so that the checkmark is displayed along with the expected strikethrough for text content. This "Preview as completed" directly updates the state of a checklist allowing otherblocks
that display such as the Progress block
to also update accordingly.
Checklist Items can have a workflow
experience linked to it allowing for this experience to be started when clicked. This is useful for cases where an experience shows up only once or is more applicable to user interaction. The option to redirect the user to the appropriate URL is also possible with this action in order to provide the intended experience. For most cases with a connected experience the corresponding Checklist Item will automatically display its checked state when completed. However, for some other cases it is more desirable to connect the completion state of the Checklist Item to a separate Goal or just allow the user to manually mark it as complete. Both of these cases can be configured / combined for these Checklist Items providing a variety of different interaction avenues.
As this block
is our flagship and most complex feature in the aptly named Checklist Experience (a.k.a. widgets
), at launch the actions for a Checklist Item are available only to paying customers. Styling for the Checklist Item block
and all other features within the different block
types are accessible to everyone. This feature proved to be our most capable addition as it provides a medium for our customers to leverage their library of experiences in a dynamic and interactive center for the user.
Blocks
- Spacer The Spacer block
is a simple component that is used inbetween blocks
to provide customizable spacing through adjustment of the Spacer block
height.
Blocks
- Divider The Divider block
is very much like the aforementioned spacer block
in that it provides distinction between blocks
through a solid divider line. This block
has customizable attributes ranging from its line thickness, line color, space above, and space below.
The Lou Builder provides an interface to build out these widgets
in an intuitive and interactive way. One of these attributes is the outline present around the block
or container
which is used to indicate hover or focus on the item that is to be edited. This tool also provides a couple of additional functions of which include displaying the block
type, buttons to adjust ordering of block
, and button to delete block
. By clicking on specific parts of the widget
, our client is able to navigate through the many of their created blocks
and go about editing with the tools provided in the toolbar.
This outline feature can be divided into two separate parts; one outline for the block
or item that is focused upon and another outline for the client is currently hovering over. Both of these outlines share a couple of common features such as dynamically changing the height, width, and positioning based on their target (i.e. blocks
and container
). These outlines also leverages the capabilities of styled-components
to create the transition styles that provide fluid movement between the items which are hovered or focused upon.
The hover outline is the basic component for providing an slight outline around the block
or container
that will be focused upon when clicked. One way to provide this outline would be to utilize the border
CSS Properties for each element but this would mean that the outlines for each block
would be a separate element. This would make it overly difficult to emulate the fluid motion for the outline since each outline would be tied to a block
or container
.
Another approach would be to have a single outline component which would make the fluid motion feasible with just the CSS Property transition-duration: 250ms;
. This makes the styling for these transitions fairly straightfoward but shifts the brunt of the load to be done using React. This is primarily done by listening to mouse events which will provide height, width, and position, details about the target element. With this information, the state of the outline component will be updated to display at the correct position through the appropriate mouse events.
The focus outline extends and builds off of the structure of the hover outline by using the same logic and adding in a couple features. Tweaks were made to the React logic to manage some of the edge cases when selecting text or using the associated buttons. The outline component uses a pseudo element to provide the text associate with the block
type such as the Checklist Item, Content, and etc. Along with this the buttons on the side of provide options to move the block
up, down, or remove it completely. Other small touches include hiding hover outline on focused outlines and removing the appropriate up and down buttons when necessary.
This addition proved to be a fairly complex feature for the Lou Builder as there were a couple of different ways to achieve the desired outcome, each with their tradeoffs. Opting to use React for the outline logic proved to work well with this feature as it provided a fast, responsive outline transitions while adhering to a design that is extensible to changes.
Up to this point all Experiences have been the same regardless of the screen size of the browser when viewed. For instances where the screen size is smaller than 700px
(i.e. Mobile and Tablet devices), the Experience has been hidden and not shown to the user. This is done since we cannot guarantee that the Experience will show up as the client intended for some of these screen sizes as the layout of the website may have changed to accomodate the smaller screen size. In this Screen Size feature for Experiences (more specifically Workflows
) we want allow the client to create Experiences that are customized appropriately for each size range of the screen, allowing the Experience to be shown on a range of devices from Mobile to Desktop.
In order to achieve this the Workflow
Model is updated and extended to essentially allow multiple Experiences to be stored inside one Workflow
. These "Experiences" would only show up under certain size range conditions and could have different Workflow Steps
associated with it. As such, internally we refer to these as Variants
as a variant of the experience is displayed when a certian screen size is met. In addition to this, the UI for the Lou Builder needed to redesigned in a way to indicate that the client is making changes to a specific variant of the Experience. For this portions of the build process such as the browser window and sidebar pages were overhauled to properly indicate which variant
was being changed.
Variant
Workflows
were initially made to have only one set of steps
that would be shown to the user during the Experience. As mentioned before, workflows
would be updated to allow for more than one set of steps
to be associated with an Experience leading this to be referred to as a workflow variant
. For this some things needed to be taken into consideration, one of which was migrating the existing workflows
to utilize additional screen size ranges.
For this case previously existing workflows
would behave exactly the same as before. The only change the client will see is the configuration that was implicitly set (i.e. Experience is not shown for screen sizes under 700px
) is now configurable.
The model for variants
is similar to that of a workflow
in that it references many of the same models for goal
, steps
, and type
. To allow for specific display conditions, a couple of extra keys are included of which include screenSizeRangeMaxWidth
, screenSizeRangeMinWidth
, and screenSizeRangeType
. These values are used to identify the screen size range in which this variant
will appear and can be adjusted accordingly.
Each variant
associated with a screen size range has a minimum width that the Experience will show up under and a maximum width that it would stop appearing at. Another assumption made with this variant
structure is that they are adjacent to one another. This would mean that the screenSizeRangeMaxWidth
value would be 1px
less than the screenSizeRangeMinWidth
value to the variant
to the right.
min max min max |---variantA---| |---variantB---| null 400px 401px 1000px
With this considerations in mind and the designs laid out in the mockups, a range slider would suit best as an interface to manage the screen size ranges of each variant
. Unfortunately the native HTML element <input type="range">
does not support multiple range sliders and a specialized component for this had to be created or imported. For this rc-slider
package proved to be a solid solution as it provided a simple component for managing multiple different range sliders seamlessly with plently of exposed props
to customize to our needs.
rc-slider
Multi Range with Custom track example. As for the values that these "Thumbs" represent, two adjacent screenSizeRangeMinWidth
and screenSizeRangeMaxWidth
values can be derived through the assumption that values are only 1px
apart. With this, an array of screenSizeRangeMinWidth
is provided to the <Slider />
component imported from rc-slider
and the callback values can be utilized as the expected screen size range values.
This then updates the appropriate values and the changes are reflected in the interface for each variant
above the slider. The CSS for each list item providing information regarding each variant
is updated to provide width values matching the "Thumb" placements within the slider below. The appropriate width
and margin-left
values are calculated by determining the percentage of space each variant's
screen size range take relative to the total maximum width. The result is a slider that provides more visual feedback to the screen size range of each variant
.
Another small feature behavior implemented within the slider is the ability to directly input the desired screen size ranges into the "Thumbs" of the slider. This is useful for many cases as clients usually have their screen size breakpoints already predefined within their website. This would allow for the client to simply enter these values into the "Thumbs" instead of arduously arraging the slider to their desired layout. This is achieved through the utilizing the exposed <Slider.Handle />
component from rc-slider
and customizing it to allow an <input />
element to be positioned within. Within the component for the custom "thumb", the same callback for handing value changes is utilized for our <input />
element is used but debounced so that it waits 1 second from the last user input to update screen size range values.
Along with the <input />
element recently added variants
also include an option to delete. This is useful for in cases where an added variant
is no longer desired and should be easily removed. Notably the option to delete already existing (saved) variants
is absent. This is a user constraint implemented to prevent the client from accidently deleting a variant
that they spent considerable time building. Another constraint is that only the first or last slider "thumb" can be deleted at a time. This is a cursory check to make sure that each screen size range is adjacent to one another.
Lastly the behavior to add in new screen size ranges is provided through as dropdown menu which provides the options of adding preset ranges for mobile, tablet, or desktop accordingly.
There are many ways to create a simple dropdown component and the easiest way is to utilize the useState
API from react to manage the display state of the dropdown through a button. This works as long as the button element is pressed but does not hide the dropdown if anything else is clicked. An approach to solve this issue would be to attach an event listeners higher up in the document to update the corresponding state for the dropdown but this comes at the cost of simplicity as managing this behavior can become a headache quite easily. Another approach would be to avoid state entirely and manage the display state of the dropdown with CSS. To achieve this the pseudo class for :focus
is utilized as a toggle for the visibility
property such that the dropdown appears when the button is focused upon. The visibility
CSS property is use instead of the similar looking display
CSS property to allow for the callbacks within the dropdown to execute before disappearing. With this the state management within the dropdown is no longer an issue and the behavior of the dropdown are as expected.
Back to the slider, adding new variants
through the previously mentioned dropdown will append the new variant
to the appropriate end. (i.e. mobile will be placed toward the left and desktop will be placed to the right.) When adding a new variant
the properties of the previous variant
such as steps are copied to the new. This is done since we expect that the client will make a minor of changes to the Experience to accomodate the respective screen size.
As with styles for Experiences, default screen size ranges can be applied to an Experience immediately after creation. This allows the client to save the commonly used screen size ranges such as their own breakpoints and reuse them in future workflows
. For this convenience is key and as such the default screen sizes can be directly edited within the Lou Builder, saving the client from the need to switch between their site and Lou Dashboard browser tabs.
Once the option to edit the default screen sizes is selected, the modal shrinks and a scroll up transition is applied to the slider. This animated change between the two is done to better convey that the client is now updating default screen sizes as the sliders look and function the same. In the provided slider, the default screen size ranges can be configured similar to a typical workflow
with the only difference here being that there is a limit of 5 different ranges. Once the default values are saved, these default screen size range values can be applied to the workflow
and saved.
These default values can also be seen within the Lou Dashboard in the Design page alongside the Themes feature used to automatically style Experiences.
Since there are multiple variants
and corresponding steps with each a clear indicator is needed to denote which variant
is being built. For this a footer component is implemented in on the Build Tab of the sidebar listing all the variants
associated with the workflow
. With this footer component the client is able to navigate through the existing variants
with left and right chevron buttons. These buttons will appear only if the amount of created variants
(3+) overflow past the width of the sidebar and make use of manipulating the scrollLeft
value to display the appropriate variant
.
In addition to selecting which variant
to focus on building, the option to delete is present on the top right of first and last variant
list items. This option is present here as opposed to the previously mentioned Slider in order to provide a confirmation check to the client before deleting the selected variant
.
In order to provide the client a environment that they would expect their Experience to appear on, the browser window automatically adjust to the associated screen size range. This allows the elements on the website to respond as expected when shown on devices with different widths. This behavior is only possible through the exposed chrome extension APIs which allow the Lou Builder to dictate the dimensions of the browser window.
Once the client is finished editing the associated Experience, the Lou Builder will automatically resize the window back to its previous dimensions. This additional behavior reduces the amount of clicks the client need to make when building an Experience and provides a more natural user experience.
Another common feature request is the support for a more in depth interactive survey workflow
step where a user can provide feedback to the completed Experience. This would be its own workflow
step type existing as a feedback
step type where in its current state provides a "Thumbs Up" or "Thumbs Down" interaction option. This has been sufficient for most interactions but could be further expanded into other areas such as "Short Answer" and "0 - 10 Score" providing more areas to gather user feedback and compile into a Net Promoter Score (NPS).
These survey step types offer more customizability than the previous feedback step, more specifically in the text and color of the associated buttons. For this the model for data saved associated with a workflow
was updated to save more specific properties for buttons and text. Some of the survey types such as the "0 - 10 Score" and "Short Answer" utilize multiple text and button properties and as such these have been prefixed with survey_primary
and survey_secondary
. Settings specific only to the feedback
type of a workflow
such as the "0/500" character limit in "Short Answer" survey are prefixed with survey
.
# General primary button and text properties.
primary_button_background_color = models.CharField()
primary_button_border_radius = models.IntegerField()
primary_button_size = models.CharField()
primary_button_text = models.CharField()
primary_button_text_color = models.CharField()
# General secondary button and text properties.
secondary_button_background_color = models.CharField()
secondary_button_border_radius = models.IntegerField()
secondary_button_redirect_url = models.CharField()
secondary_button_size = models.CharField()
secondary_button_text = models.CharField()
secondary_button_text_color = models.CharField()
# Survey specific primary / secondary button and text properties.
survey_primary_button_background_color = models.CharField()
survey_primary_button_text_color = models.CharField()
survey_primary_text = models.CharField()
survey_primary_text_color = models.CharField()
survey_secondary_text = models.CharField()
survey_secondary_text_color = models.CharField()
survey_type = models.CharField()
With these properties, each workflow
survey step can be customized to best fit the client's needs. To further assist the creation process a template for creating "NPS" Experience is avaialable on the Templates page of the Lou Builder. Like a general Experience, the dashboard provides pages to configure user segments and view associated analytics. The analytics page has a special section specific to this feature to provide a visual graph of the "Net Promoter Score" and table highlighting the user responses and scores. This feature proved a success with our clients and a nice selling point as it provided a means to record more descriptive user feeedback and better visuals to interpret these results.