Guided Tours Blazor component

In this new post, I introduce you my new Guided Tours Blazor component for Blazor WebAssembly and Blazor Server to help your users to understand the UI of your application.

Scenario

Have you ever visited a website or a web application, where you don’t have the most remote idea of how to start using it because it’s your first time there?

As a web developer, you may want to avoid this experience from your user creating some type of guide video, or a well explained documentation. However, some users won’t read the documentation or see a video because there is no time for that, they just want to use your app! For this kind of users (that’s the 90% of the people) you can use a dynamic in web tour while they learn to use your app. You can implement this feature by using a Guided Tour plugin in your app.

JavaScript libraries

There are some libraries for JavaScript like:

  • Anno is a step-by-step guides plugin for powerful web apps. Anno.js is built to be extensible, the source is about 500 lines of literate coffeescript; you can read the annotated source in just a few minutes
  • jQuery Guide is a jQuery plugin made to create a “How to use guide” for your web app. It uses jQuery animations to provide a smooth and nice experience for the user while they learn how to use your app
  • aSimpleTour is a jQuery plugin that will help you to make website tours easily
  • Pageguide is a plugin to create interactive guide for web page elements using jQuery and CSS3. Instead of cluttering your interface with static help message, or explanatory text, add a pageguide and let your users learn about new features and functions. Pageguide comes with an example implementation (the files are in /example) which you can run locally with Grunt
  • Intro.js is a Step-by-step guide and feature introduction plugin for your website. When new users visit your website or product you should demonstrate your product features using a step-by-step guide

All of them are very nice libraries but there is nothing for Blazor. So, I have created one and the result is here.

Add Guided Tours to your project

First, download and install the package from NuGet. Then, register the service in your project in 2 ways.

Startup.cs

In your Startup.cs you call UseTour() function in the ConfigureServices like in the following code

using PSC.Blazor.Components.Tours;

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.UseTour();

    // ...
}

NET6 configuration

If your project is built in NET6, add to your Program.cs the call to UseTour() like in the following example

using PSC.Blazor.Components.Tours;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");

builder.Services.AddScoped(sp => new HttpClient { 
    BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) 
});
builder.Services.UseTour();

await builder.Build().RunAsync();

Add style

In your index.html add the following code to add the generic style in the HEAD section of the page

<link rel="stylesheet" href="/_content/PSC.Blazor.Components.Tours/css/styles.css" />

Now, the configuration is completed. So, we are ready to create the first tour.

Create a tour

For example, in your page you have a div like to following

<div data-first>
    <span>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do 
        eiusmod tempor incididunt ut labore et dolore magna aliqua. 
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi 
        ut aliquip ex ea commodo consequat. 
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit 
        anim id est laborum.
    </span>
</div>

Nothing special apart from the data tag at the end of the line. The data attribute allows the Guided Tours Blazor component to identify the DOM object to highlight and use to add the help balloon. Now, add the code to create the tour.

<GuidedTour TourId="FormGuidedTour" OverlayEnabled="true">
    <GuidedTourStep Title="My First Step" StepName="firstStep" TourStepSequence="1" 
        ElementSelector="[data-first]">
        <span>This is my first step</span>
    </GuidedTourStep>
</GuidedTour>

It is important to give to the tour a TourId that allows you to start the tour. Every tour can have one or more GuidedTourStep. Each GuidedTourStep has a title, a StepName and a TourStepSequence. Also, it has an ElementSelector: this refers to the data attribute in a HTML tag. The component looks this data attribute in the page and display the help.

Finally, we have to start the tour. For that, add this button

<button class="btn btn-primary" type="button" @onclick="StartTour">Start Tour</button>

and the following code

@code {
    [Inject]
    private ITourService TourService { get; set; }

    private async Task StartTour()
    {
        await TourService.StartTour("FormGuidedTour");
    }
}
Basic Guided Tour with the Blazor component - Guided Tours Blazor component
Basic Guided Tour with the Blazor component

Guided Tour Component

The Guided Tour component is the main visual component that displays a tour on the page. Multiple tours can be registered as long as they have unique Id’s. The GTour component inherits from a base component that in turn implements an interface to ease development and extensibility.

Adding a Tour

To add a tour to a razor page, import the namespace if not globally imported and add the component to the page. Below is an example of all the properties available on the tour component. The Arrow Element details can be found on the respective help page. The child content holds the tour steps and can be found on the respective page.

<GuidedTour TourId="Tour1" OverlayClass="overlay-class" CloseOnOverlayClick="true" 
            HighlightClass="my-highlight-class" HighlightEnabled="true" TourWrapperClass="tour-wrapper-class"
            ArrowClass="my-arrow-class" OverlayEnabled="true" ShowArrow="true"
            OnTourCanceled="@((tour) => {})" OnTourCompleted="@((tour) => {})"
            OnTourStarted="@((tour) => {})" OnTourStepRegistered="@((tourStep) => {})"
            OnTourStepDeRegistered="@((tourStep) => {})">
     <ArrowContent>
         ...
     </ArrowContent>
     <ChildContent>
         ...
     </ChildContent>
</GuidedTour>

Properties

  • TourId: The unique identifier of the tour.
  • ArrowClass: An override class name to style the arrow
  • CloseOnOverlayClick: A value to indicate if the tour should be stopped when clicking on the overlay
  • HighlightClass: A class to be appended to a Tour Step Element that is “Focused”.
  • HighlightEnabled: A value to indicate if the highlight class should be appended to the focused element
  • OverlayClass: An additional class to append to the overlay
  • OverlayEnabled: A value indicating if the overlay is enabled for the tour
  • ShowArrow: A value to indicate if the arrow should be rendered for the tour component
  • TourWrapperClass: An additional class to be appended to the root tour element

Callbacks

  • OnTourCanceled: A callback for when the tour is canceled
  • OnTourCompleted: A callback for when the tour is completed
  • OnTourStarted: A callback for when the tour has started
  • OnTourStepRegistered: A callback for when a child tour step has been registered
  • OnTourStepDeRegistered: A callback for when a child tour step has been deregistered

Adding a Tour Step

To add a tour step to a razor page, create a new component in the Guided Tour ChildContent tag. Even though the sequence is not required to be set is highly advisable that this property be set. The tour step name is also a required property that needs to be set.

<GuidedTour ...>
     <ChildContent>
         <GuidedTourStep    
           Title="Custom Buttons" StepName="stepName" TourStepSequence="1" ElementSelector="[data-element-to-select]"
           CancelTourButtonClass="cancel-btn-class" CanCancelTour="false" HeaderClass="my-hdr-class" FooterClass="my-ftr-class"
           ContentClass="my-content-class" CompleteTourButtonText="Complete Tour" CompleteTourButtonClass="complete-btn-class"
           CancelTourButtonText="Cancel Tour" SkipStep="false" NextStepButtonClass="next-btn-class" NextStepButtonText="Next Step"
           OnNavigateNext="@((tourStep) => {})" OnNavigatePrevious="@((tourStep) => {})" OnStepActivated="@((tourStep) => {})" 
           OnStepDeActivated="@((tourStep) => {})" OnTourCanceled="@((tourStep) => {})" OnTourCompleted="@((tourStep) => {})"
           PopupPlacement="PopperPlacement.Auto" PopupStrategy="PopperStrategy.Absolute" PreviousStepButtonClass="previous-btn-class" PreviousStepButtonText="Previous Step"
           WrapperClass="my-step-wrapper">
         <HeaderContent>    
             <div><span>My Header</span></div>    
         </HeaderContent>    
         <ChildContent>    
             <div>...</div>    
         </ChildContent>    
         <FooterContent>    
             <div>    
                 <button class="btn" @onclick="@(() => context.GoToStep("firstStep"))">First Step</button>    
                 <button class="btn" @onclick="@(() => context.PreviousStep())">    
                     <i class="bi bi-cloud-drizzle"></i><span>Prev</span>    
                 </button>    
                 <button class="btn" @onclick="@(() => context.CompleteTour())">Complete</button>    
             </div>    
         </FooterContent>    
     </GuidedTourStep>    
  </ChildContent>
</GuidedTour>

Properties

  • StepName: The unique name of the step
  • TourStepSequence: The sequence of this step within the tour
  • ElementSelector: Optional element selector for this step, this will set where this step is displayed
  • CancelTourButtonClass: An additional class to append to the default cancel tour button
  • CancelTourButtonText: The text for the cancel tour button
  • CanCancelTour: A value indicating if this step can cancel the tour
  • ChildContent: Custom content to render for the child step. The @context variable can be used to reference the tour step
  • CompleteTourButtonText: The text for the complete tour button
  • CompleteTourButtonClass: An additional class to append to the default complete tour button
  • ContentClass: An additional class to append to the content wrapper
  • FooterClass: An additional class to append to the footer wrapper.
  • FooterContent: Custom content to render as the footer. The @context variable can be used to reference the tour step
  • HeaderClass: An additional class to append to the header wrapper
  • HeaderContent: Custom content to be rendered in the header. The @context variable can be used to reference the tour step
  • NextStepButtonClass: An additional class to append to the default next step button
  • NextStepButtonText: The text for the next step button
  • PopupPlacement: The position of the tour step
  • PopupStrategy: Can either be absolute or fixed
  • PreviousStepButtonClass: An additional class to append to the default previous step button
  • PreviousStepButtonText: The text for the previous step button
  • SkipStep: A value indicating if the step should be skipped
  • Title: If a custom header is not supplied, this will be the title of the step
  • WrapperClass: An additional class to append to the main wrapper

Callbacks

  • OnNavigateNext: A callback for the tour is navigating to the next step
  • OnNavigatePrevious: A callback for the tour is navigating to the previous step
  • OnStepActivated: A callback for when the tour has been activated
  • OnStepDeActivated: A callback for when this step has been de-activated
  • OnTourCanceled: A callback for when the tour is canceled
  • OnTourCompleted: A callback for when the tour is completed

Tour Service

The TourService is registered as a singleton service and can only have one active tour started at any time. When the StartTour method is called while there is an existing tour active, the first tour will be stopped first.

Depending on what step the tour is on, this can be either TourCompleted or TourCanceled. If the tour is on the last step the TourCompleted will be called, if not the TourCanceled will be called. The service is registered in the Dependency Injection Container on startup. See the example below to register the service.

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.UseTour();

    // ...
}

Methods

The TourService has the following methods available and the tour can be controlled from this point. This is especially helpful when navigating from custom content in the tour steps.

  • RegisterTour(ITour gTour)This is called internally and will automatically be called once a tour is included on a razor page.
  • StartTour(string tourId, string startStepName = default)Tries to start a tour with the given name and optionally the first step of the tour
  • StartTour(ITour gTour, string startStepName = default)Tries to start a tour with the interface reference and optionally the first step of the tour
  • StopTour()Stops the currently active tour. If the Tour is on the last step, the tour will be completed. If not it will be canceled
  • CancelTour()Cancels the current active tour
  • PreviousStep()Navigates to the previous step in the active tour if there is a step prior
  • GoToStep(string stepName)Navigates to the step if it exists
  • CompleteTour()Completes the tour
  • DeRegisterTour(ITour gTour)This is called internally and will automatically be called once a tour component is disposed.
 <GuidedTour ...>
 <GuidedTourStep ...>
     <ChildContent ...>
     ...
     </ChildContent>
     <FooterContent>
         ...
         <button class="btn" @onclick="@(() => TourService.CompleteTour())">Complete</button>  
     </FooterContent>
 </GuidedTourStep>
</GuidedTour>
[Inject]
private ITourService TourService { get; set; }

private async Task StartTour() 
{
    // ...
    await TourService.StartTour("TourName", "startAtStep");    
    // ...
}

Events

The TourService has a few events that can be used to help drive the tour as well as assist in logging output.

  • OnTourRegistered
  • OnTourDeRegistered
  • OnTourStarting
  • OnTourStarted
  • OnTourCanceling
  • OnTourCanceled
  • OnTourCompleting
  • OnTourCompleted
@implements IDisposable

protected override void OnInitialized()
{
    base.OnInitialized();
    TourService.OnTourRegistered += OnTourRegistered;
}

private void OnTourRegistered(ITourService sender, ITour tour)
{
    Console.WriteLine($"Tour with name {tour.TourId} registered");
}

public void Dispose()
{
    TourService.OnTourRegistered -= OnTourRegistered;
}

Arrows

An arrow indicator has been included on the Tour Steps. It looks ugly yeah but the arrow should rather be an icon element or not show at all. So this is how you do it.

No Arrow

To turn of the Arrow Element rendering, set the Show Arrow Element on the guided tour

<GuidedTour TourId="DefaultTheme" OverlayEnabled="true" ShowArrow="false">
</GuidedTour>

Default Arrow Style

This is the default style applied to the arrow and can be overwritten by either overwriting the classes or setting the ArrowClass property on the Guided Tour.

.arrow, .arrow::before {
    position: absolute;
    width: 8px;
    height: 8px;
    background: inherit;
}

.arrow {
    visibility: hidden;

    &.force-hide::before {
    visibility: hidden !important;
    }
}

.arrow::before {
    background: white;
    visibility: visible;
    content: '';
    transform: rotate(45deg);

    &.force-hide {
    visibility: hidden !important;
    }
}
<GuidedTour TourId="DefaultTheme" OverlayEnabled="true" ArrowClass="myArrowClass">
</GuidedTour>

Custom Icon

This is the prefered method of showing the arrow element. Take not of the data attribute data-popper-arrow. This allows popperJs to pick up the arrow element and position it accordingly. In the below sample a bootstrap icon is used as the arrow element. This is accompanied with a style to rotate the icon element.

<GuidedTour TourId="DefaultTheme" OverlayEnabled="true" ArrowClass="myArrowClass">
    <ArrowContent>
        <i class="my-arrow bi bi-caret-up-fill" data-popper-arrow></i>
    </ArrowContent>
</GuidedTour>
.guided-tour-wrapper[data-popper-placement^='top'] > .my-arrow {
    bottom: -16px;
    transform: rotate(180deg) !important;
}

.guided-tour-wrapper[data-popper-placement^='bottom'] > .my-arrow {
    top: -12px;
}

.guided-tour-wrapper[data-popper-placement^='left'] > .my-arrow {
    right: -12px;
    transform: rotate(90deg) !important;
}

.guided-tour-wrapper[data-popper-placement^='right'] > .my-arrow {
    left: -12px;
    transform: rotate(-90deg) !important;
}

Overlay

An overlay can be displayed when the tour is active, by default this is on. There is also an option to stop the tour when the overlay is clicked. The overlay element class can also be set with the OverlayClass property.

Disabling the Overlay

The overlay can be disabled by setting the OverlayEnabled property on the Tour.

<GuidedTour TourId="DefaultTheme" OverlayEnabled="false" CloseClickOverlay="false" >
</GuidedTour>
Disabling the Overlay
Disabling the Overlay

Close on Click

To close the tour when the overlay is clicked, set the CloseOnOverlayClick to true. If the tour is on the last step, it will internally call the TourCompleted action and if not, it will call the TourCancelled action.

<GuidedTour TourId="DefaultTheme" OverlayEnabled="true" CloseClickOverlay="true" >
</GuidedTour>
Close on Click
Close on Click

Theming

The Guided Tour package comes with 3 built in themes. It also allows for custom themes to be built.

  • None – This will apply a blank theme to the component and all styles should be set.
  • Default – This is the default built in theme.
  • Bootstrap – Bootstrap classes are used for the theme.

Adding a Custom Theme

Create a new theme class and inherit from the ITheme. Implement the class names to use. Below is an example of the Bootstrap implementation.

public class MyOwnCustomTheme : ITheme
{
    public string TourOverlay { get; set; }
    public string TourWrapper { get; set; }
    public string TourArrow { get; set; }
    public string TourStepWrapper { get; set; } = "modal-content ";
    public string TourStepHeaderWrapper { get; set; } = "modal-header ";
    public string TourStepContentWrapper { get; set; } = "modal-body ";
    public string TourStepFooterWrapper { get; set; } = "modal-footer ";
    public string TourStepHeaderTitle { get; set; } = "modal-title ";
    public string TourStepCancelButton { get; set; } = "btn btn-warning ";
    public string TourStepPreviousButton { get; set; } = "btn btn-secondary ";
    public string TourStepNextButton { get; set; } = "btn btn-primary ";
    public string TourStepCompleteButton { get; set; } = "btn btn-success ";
}

By Enrico

My greatest passion is technology. I am interested in multiple fields and I have a lot of experience in software design and development. I started professional development when I was 6 years. Today I am a strong full-stack .NET developer (C#, Xamarin, Azure)

This site uses Akismet to reduce spam. Learn how your comment data is processed.