Getting Started Storybook (Latest update)

A while back I posted Getting started with Storybook. However, like with most things JavaScript, things have changed, and those instructions are now out of date and also, now, much simpler.

I have an existing React web app. so how do I add storybook ?

Note: Do not add storybook from NPM via yarn/npm, instead use the following instructions.

From you web app’s root folder run the following from the root of an existing project

npx storybook init

This will try to detect the framework being use. In the case of React this worked a treat. It will then add a stories folder to your src folder with a bunch of examples. It adds the .storybook folder with the information to tell storybook what files to look for and, ofcourse, add all the package.json dependencies etc.

Now all you need do is run

yarn storybook

So simple.

Here’s a very simple example of a .stories.tsx. I have a Header component which simpler writes a string out for a given date.

import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { Header } from '../controls/Header';

export default {
  title: 'Header',
  component: Header,
  parameters: {
    layout: 'fullscreen',
  },
} as ComponentMeta<typeof Header>;

const Template: ComponentStory<typeof Header> = (args) => <Header {...args} />;

export const Display = Template.bind({});
Display.args = {
   endDateTime: new Date(Date.parse("2022-12-31T23:59:00.000Z"))
};

The Display export is shown as a view on a component within storybook and we also get the endDateTime editor for trying different inputs out.

A few tips and tricks for using Infragistics XamDataGrid

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

I’m working on a project using the XamDataGrid. I’ve used the UltraWinGrid by Infragistics in the past but that doesn’t help at all when moving code from Windows Formds to WPF. So here’s a list of a few commonly required features and how to do them using the XamDataGrid.

Note: this post only refers to using version 12.2

Grid lines

Problem: By default there are no grid lines on the XamDataGrid.

Solution: In your ResourceDictionary (within Generic.xaml or wherever you prefer) add the following

<Style TargetType="{x:Type igDP:CellValuePresenter}">
   <Setter Property="BorderThickness" Value="0,0,1,1" />
   <Setter Property="BorderBrush" Value="{x:Static SystemColors.ControlLightBrush}" />
</Style>

Obviously replace the BorderBrush colour with your preferred colour.

Remove the group by area

Problem: I want to remove the group by section from the XamDataGrid.

Solution: In your ResourceDictionary (within Generic.xaml or wherever you prefer) add the following

<Style x:Key="IceGrid" TargetType="igDP:XamDataGrid">
   <Setter Property="GroupByAreaLocation" Value="None" />
</Style>

don’t forget to apply the style to your grid, i.e.

<dp:XamDataGrid Style="{StaticResource IceGrid}" DataSource="{Binding Details}">
   <!- Your grid code -->
</dp:XamDataGrid>

Column formatting

Problem: We want to change the numerical formatting for a column

Solution: We can set the EditorStyle for a field (editor doesn’t mean it will make the field editable)

<dp:XamDataGrid.FieldLayouts>
   <dp:FieldLayout>
      <dp:FieldLayout.Fields>
         <dp:Field Name="fee" Label="Fee" Width="80">
            <dp:Field.Settings>
               <dp:FieldSettings>
                  <dp:FieldSettings.EditorStyle>
                     <Style TargetType="{x:Type editors:XamNumericEditor}">
                        <Setter Property="Format" Value="0.####" />
                     </Style>
                  </dp:FieldSettings.EditorStyle>
               </dp:FieldSettings>
           </dp:Field.Settings>
        </dp:Field>         
      </dp:FieldLayout.Fields>
   </dp:FieldLayout>
</dp:XamDataGrid.FieldLayouts>

This code creates a field named fee and with the label Fee and the editor is set to only display decimal places if they actually exist.

As we’re defining the fields you’ll need to turn off auto generation of fields, as per

<dp:XamDataGrid.FieldLayoutSettings>
   <dp:FieldLayoutSettings AutoGenerateFields="False" />
</dp:XamDataGrid.FieldLayoutSettings>

First Chance Exceptions can be more important than you might think

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

You’ll often see First Chance exceptions in the output window of Visual Studio without them actually causing any problems with your application, so what are they ?

A first chance exception is usually associated with debugging an application using Visual Studio, but they can occur at runtime in release builds as well.

A first chance exception occurs whenever an exception occurs. The exception is thrown at which point .NET searches for a handler (a catch) up the call stack. If a catch is found then .NET passes control to the catch handler code. So in this instance, let’s say we see a first chance exception which is caught in some library code. Then, whilst debugging, we’ll see the message that a first chance exception occurred, but it won’t cause an exception to halt an application because it’s caught. On the other hand if no catch is found the exception becomes a second chance exception within the debugging environment and this causes the debugger to break (if it’s configured to break on exceptions).

We can watch for first chance exceptions by adding an event handler to the AppDomain.CurrentDomain.FirstChanceException, for example

AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;

private void CurrentDomain_FirstChanceException(
    object sender, FirstChanceExceptionEventArgs e)
{
   // maybe we'll log e.Exception
}

So first chance exceptions don’t tend to mean there’s problems within your code however using async/await, especially with void async methods you may find first chance exceptions are the a precursor for an unhandled exception from one of these methods.

If you check out Jon Skeet’s answer on stackoverflow to Async/await exception and Visual Studio 2013 debug output behavior he succinctly describes the problems that can occur with exception handling in async/await code.

To use this great quote “When an exception occurs in an async method, it doesn’t just propagate up the stack like it does in synchronous code. Heck, the logical stack is likely not to be there any more.”. With the async/await on a Task, the task itself contains any exceptions, but when there is no task, when we’re async/await on a void method, then there’s no means to propagate any exceptions. Instead these exceptions are more likely to first appear as First Chance exceptions and then unhandled exceptions.

See also https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Xamarin.Forms lifecycle

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

The following is an example of what’s generated as part of the Xamarin.Forms project.

public class App : Application
{
   protected override void OnStart()
   {
      // Handle when your app starts
   }

   protected override void OnSleep()
   {
      // Handle when your app sleeps
   }

   protected override void OnResume()
   {
      // Handle when your app resumes
   }
}
  • OnStart is called when the application starts
  • OnSleep is called when the application goes into sleep mode and/or terminates
  • OnResume is called when an application is resumed after a sleep

Swift Tasks

Swift uses the Task struct to execute code concurrently. We can run a Task and await the return value or we can use in a “fire and forget” manner. We create a task like this

Task {
   // do something
}

We do not need to call a start method or the likes, once created the code starts. If you intend to return a value you’d write something like this

let result = await Task { () -> String in
   "Hello World"
}.value

Ofcourse we might also have the possibility of throwing an exception, hence we’d use

do {
   let result = try await Task { () -> String in
      throw MyError.failed
   }.value
} catch {
}

Deploying a Xamarin Forms application to TestFlight

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

Pre-requisites

You’ll need an Apple developer account if you intend to eventually deploy your application to the Apple store, using this site https://developer.apple.com/account, however you can use the free provisioning using Free provisioning for Xamarin.iOS apps.

As I already have an Apple Developer account this post will concentrate on provisioning with a Developer account id.

TestFlight

TestFlight is the Apple beta testing tool which allows users to access your application in test (i.e. not live on the app store). Your users will need to install TestFlight, see Beta Testing Made Simple with TestFlight and you’ll need to set up an application for testing via https://appstoreconnect.apple.com/

Deploying your application to TestFlight

Once you’ve built and signed your application so it’s ready for distribution then, one of the simplest ways to deploy your application to TestFlight is install Transporter on your Mac and simply Add the application to Transport which in turn will upload to your TestFlight account.

Your application will go through a build process and when completed you’ll receive an email letting you know. You’ll be waiting for the status of your build (within appstoreconnect) to change state to Approved (this may take a couple of days although I’ve also seen posts from people stating this can take longer). Once approved we select our External Groups (testers) and you should see a Enable Public Link – click on this (enable it). Now you’ll have a link to send to your users, although appstoreconnect should automatically send this to any testers you add.

Note: Your TestFlight build is available for a total of 90 days, ofcourse this excludes the build and review process. If you do not update the application within that time the app will expire.

Once your build has passed review, it’ll go into a Testing status, any testers you’ve added already will get a code to redeem in TestFlight from their device, you can also enable a link that you can send to people to load the application for testing. This obviously allows you to have a core group of testers as well as a public access to testing.

When your application is deployed to your testers via TestFlight, the icon that you launch the application from will have a small orange dot (it looks red on a dark background) to indicate this is a beta application.

When you’re ready to update your testers with a new application, if you only change the Build version, i.e. if your app version is 0.1.0 and build was 1.0, just changing this to 2.0 will mean your application need not go through review again. Instead it’ll go into a Submit Ready state, waiting for you to assign testers and then it’ll switch to Testing.

References

Automatic Provisioning for Xamarin.iOS
TestFlight – How to Upload and Distribute Your App | App Store 2021

Handling “unhandled” exceptions in WPF

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

None of us want our applications to just crash when an exception occurs so we often ensure we’ve got catch blocks around possible exceptions, but ofcourse, sometimes we either don’t care to handle an exception explicitly or we forget to code the catch block or the place the exception occurs such that it’s not possible to handle in such a structured way. In such scenarios we want to handle all “unhandled” exceptions at the application level.

Let’s take a look at some of the ways to handle exceptions in a WPF application. Here’s a list some of those ways to handle “unhandled” exceptions.

AppDomain.UnhandledException

The AppDomain.UnhandledException or more specifically AppDomain.CurrentDomain.UnhandledException.

Application.DispatcherUnhandledException

The Application.DispatcherUnhandledException or more specifically the Application.Current.DispatcherUnhandledException

Dispatcher.UnhandledException

The Dispatcher.UnhandledException or more specifically Dispatcher.CurrentDispatcher.UnhandledException

AppDomain.FirstChanceException

The AppDomain.FirstChanceException or more specifically AppDomain.CurrentDomain.FirstChanceException.

TaskScheduler.UnobservedTaskException

The TaskScheduler.UnobservedTaskException

Github action build scripts for various languages

I’ve been through a bit of a wave of writing my unit of measurement library code for various programming languages, originally starting in F# through C# and Java to Go, Rust, Swift, Typescript and Python. Each time I’ve needed/wanted to create a Github actions build workflow for each language. To be honest Github gives you all the information you need, but I’m going to list what I’ve got and what worked for me etc.

Creating the workflow file

I’ve covered much of this before, but I’ll recreate here for completeness.

You’ll need to start by following these steps

  • Create a .github folder in the root of your github repository
  • Within the .github folder create a workflows folder
  • Within the workflows folder create a file – it can be named whatever you like, let’s use build.yml (yes it’s a YAML file)

The workflow configuration

Note: YAML is a format where whitespace is significant. In the code snippets below I will left justify the code, basically breaking the format but making it easier to read. At the end of the post I’ll put all the snippets together to show the correct format, if you’re just here for the code, then I’d suggest scrolling to the bottom of the post.

Your build.yml (if you followed my naming) will start with the name of the workflow, so let’s simply name it Build, i.e.

name: Build

Next up we need to list the events that cause the workflow to start, usually this will be things like “on push” or “on pull_request”. So now we add the following to the .yml file

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  workflow_dispatch: # Manual Run

As Github has moved away from using master for the root branch to main obviously change master to main in the above. I tend to (at least initially) also include a way to manually run the workflow if I need to, hence include workflow_dispatch.

So we have a name of the workflow and the events that trigger the workflow, now we need the tasks or in Github action terms, the jobs to run. So we’ll just add the following, to our .yml

jobs:
   build:

Now for the next step, we can either simply list the VM OS to run on using

runs-on: ubuntu-latest

Or, as all the languages listed at the start of this post are cross platform, we might want to try building them on each OS we want to support, in this case we create a strategy and matrix of OS’s to support. It’d also be good to see in the build what OS we’re building for so we’ll create the following

name: Build on ${{ matrix.os }}
strategy:
  matrix:
    os: [macos-latest, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}

In the above, we’ll try to run our build on Mac, Ubuntu and Windows latest OS environments. If you only care about one environment you can use the previous runs-on example or just reduce the os array to the single environment.

Note: If you take a look at virtual-environments, you’ll see a list of supported TAML labels for the OS environment.

Within the matrix we can declare variables that are used in runs-on but also for steps, so for example if we’re wanted to list the version(s) that we want to support of a toolchain or language, we could do something like

matrix:
  os: [macos-latest, ubuntu-latest, windows-latest]
  go-version: [1.15.x]

So now we have matrix.go-version available for deciding which version of Go (for example) we’ll install.

We’ve setup the environments now we need to actually list the steps of our build. The first step is to checkout the code, so we’ll have something like this

steps:
- uses: actions/checkout@v2

Next we’re create each step of the build process, usually with a name for each step to make it easier to see what’s happening as part of the build workflow. At this point we’ll start to get into the specifics for each toolchain and language. For example for F# and C# we’ll start by setting up dotnet. For Swift we can just start calling the swift CLI. So let’s go through each toolchain/language setup I have for my unit conversion libraries/packages.

.NET steps

For .NET we’ll need to setup .NET with the version(s) we want to build against. Then install any required nuget dependencies, then build our code, run any tests and if required package up and even deploy to NuGet (which is covered in my post Deploying my library to Github packages using Github actions).

- name: Setup .NET Core SDK 6.0.x
  uses: actions/setup-dotnet@v1
  with:
    dotnet-version: '6.0.x'
- name: Install dependencies
  run: dotnet restore
- name: Build
  run: dotnet build --configuration Release --no-restore
- name: Test
  run: dotnet test FSharp.Units.Tests/FSharp.Units.Tests.fsproj --no-restore --verbosity normal
- name: Create Package
  run: dotnet pack --configuration Release

Note: Ofcourse change the FSharp.Units.Tests/FSharp.Units.Tests.fsproj to point to your test project(s).

Java steps

For Java we’re just going to have a setup step and then use Maven to build and run tests

- name: Setup JDK 8
  uses: actions/setup-node@v1
  with:
    java-version: '8'
    distribution: 'adopt'
- name: Build with Maven
  run: mvn --batch-mode --update-snapshots verify
[code]

<strong>Go steps</strong>

With Go we're using a previously declared <em>go-version</em> to allow us to target the version of Go we want to setup, ofcourse we could do the same for .NET, Java etc. Next we're installing dependencies, basically I want to install golint to lint my code. Next up we build our code using the Go CLI then vet and lint before finally running the tests.

[code]
- name: Setup Go
  uses: actions/setup-go@v2
  with:
    go-version: ${{ matrix.go-version }}
- name: Install dependencies
  run: |
    go version
    go get -u golang.org/x/lint/golint
- name: Build
  run: go build -v ./...
- name: Run vet & lint
  run: |
    go vet ./...
    golint ./...
- name: Run testing
  run: go test -v ./...

Rust steps

For Rust we’ll keep the steps pretty simple

- name: Build
  run: cargo build --verbose
- name: Run tests
  run: cargo test --verbose

Swift steps

- name: Build
  run: cargo build --verbose
- name: Run tests
  run: cargo test --verbose

Typescript steps

- name: Use Node.js
  uses: actions/setup-node@v1
  with:
    node-version: '12.x'
- name: Install dependencies
  run: yarn
- run: yarn run build
- run: yarn run test

Python steps

- name: Set up Python
  uses: actions/setup-python@v2
  with:
    python-version: '3.x'
- name: Test with pytest
  run: |
    python -m unittest

Google Play Testing Releases

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

The Google Play console allows you to submit your application to the Google Play store. Obviously you’ll need to go through the store for a Production release but the Google Play Console also allows you to go through testing phases with your application in a controlled manner (as opposed to just supply a .apk from your own web site or similar).

There are three testing options, Internal, Closed and Open.

Internal Testing

Internal testing allows us to pretty much just upload our .aab to the Google Play Console, supply a list of tester’s emails (these seem to need to be gmail email apart from the dev owner of the account). There is a limit of a maximum of 100 testers for Internal testing.

Once uploaded and assuming you’re supplied required information (as will be highlighted by the Internal testing page) and you’ve set up your list of testers, you can simply send a link to each tester and they will then be given access to your application (obviously the email they sign into Google Play will need to match the one supplied in your list of testers).

Internal testing is very useful in that you can put an application out to test with minimal “form filling”, no need for an application name even, or descriptions or screen shots etc.

Google Play will run a cutdown Firebase Test Lab (I believe) which will run some UI testing on your application against different devices and OS versions, take screens shots and even video on the automated interactions – this is extraordinarily useful. A repot (see Pre-launch report) will also display performance issues, accessibility etc.

Closed Testing

Closed testing can be seen as, maybe, an alpha release. It’s similar to Internal testing in that you again dictate who can access your application, but unlike Internal Testing you will need to supply assets along the lines of what your final release would look like, i.e. description of the app, images, screenshots etc.

Open Testing

One might see this as a beta release phase, after Internal and/or Closed testing you may want to open the application to a larger group of potential testers – at this stage anyone can join your test programme. If you’ve gone straight from Internal to Open testing, as per Closed Testing, you will need to supply assets, descriptions etc. that will be displayed in the Google Play store.

References

Set up an open, closed or internal test

Trying out Avalonia

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

Avalonia is aiming to support a XAML way of implementing cross platform UI’s. Whilst I can use Xamarin Forms for developing on iOS, Android etc. it doesn’t currently support Linux.

So let’s have a look at implementing a very basic Hello World application using Avalonia. This post covers using Visual Studio 2017 (obviously on Windows).

  • Creating a WPF application, mine’s named HelloAvaloniaWorld
  • Remove the following references
    • PresentationCore
    • PresentationFrameworj
    • WindowsBase

    as we’re not using WPF

  • Add NuGet packages
    • Avalonia
    • Avalonia.Desktop

Now replace App.xaml with the following

<Application xmlns="https://github.com/avaloniaui">
    <Application.Styles>
        <StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
        <StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
    </Application.Styles>
</Application>

Replace the application class in App.xaml.cs with

using Avalonia;
using Avalonia.Markup.Xaml;

namespace HelloAvaloniaWorld
{
    public class App : Application
    {
        public override void Initialize()
        {
            AvaloniaXamlLoader.Load(this);
        }

        private static void Main()
        {
            AppBuilder.Configure<App>()
                .UsePlatformDetect()
                .Start<MainWindow>();
        }
    }
}

Let’s now alter the MainWindow.xaml to look like

<Window xmlns="https://github.com/avaloniaui">
    <Grid>
        <TextBlock FontSize="16" Text="Hello World"/>
    </Grid>
</Window>

And replace the MainWindow.xaml.cs contents with

using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace HelloAvaloniaWorld
{
    public class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
            this.AttachDevTools();
        }

        private void InitializeComponent()
        {
            AvaloniaXamlLoader.Load(this);
        }
    }
}

Before we move on, select each XAML file and display the file properties in Visual Studio, from here, remove the Custom Tool and change the Build Action to Embedded Resource.

From AssemblyInfo.cs remove the section

[assembly: ThemeInfo(...)]

Before the application builds you’ll need to copy the packages\SkiaSharp.1.57.1\build and runtime folders to the solution root folder, into a folder named SkiaSharp\1.57.1.