xLayers - Rethink UX
This contribution is a new feature.
Introduction
Project
You can find the xLayers project presentation here.
Context
The main idea of the issue is to find a new workflow for the user, to rethink the UX.
From the first page to the last one.
Current behavior
Currently the user workflow looks like this:
The main issue is that xLayers generates code for all supported framework at once.
This could be problematic in the future if it supports more frameworks.
Implement the solution
- On the landing page, we will allow users to choose their framework they wanna generate code for.
- The code generation UI should only show the selected framework.
Users can switch the selected framework.
The code blocks are intentionally incomplete for the sake of readability.
If you want to read the full code you'll find it in the PR link at the top.
Update the landing page
My first suggestion for this page was to implement a carousel with all the supported frameworks.
By discussing with the other members of the team we decided to display only a simple list.
As we need to use the different CodeGenKind
(frameworks) in two different pages, I extracted a list to a shared file where we put all our CodeGenKind
elements.
Each element contains:
- a label for the text preview
- a svgIcon for the icon name
- a codegenType to reference the element
export interface UICodeGen {
label: string;
svgIcon: string;
codegenType: CodeGenKind;
}
export const codeGenList: UICodeGen[] = [
{
label: 'Angular',
svgIcon: 'angular',
codegenType: CodeGenKind.Angular,
},
{
label: 'Vue',
svgIcon: 'vue',
codegenType: CodeGenKind.Vue,
},
{
label: 'React',
svgIcon: 'react',
codegenType: CodeGenKind.React,
},
// ...
];
Update the code generation page
As this page already includes a way to switch between frameworks, my first suggestion was to remove the frameworks tab bar and replace it with a simple dropdown component which will be much more unobtrusive and it will not disturb the user. And we also save space for the code editor !
Nothing magic here, we have a mat-select
containing our list of frameworks.
The mat-select-trigger
allows us to add the framework mat-icon
inside the select.
<!-- ... -->
<mat-form-field class="framework_selection" appearance="fill">
<mat-label>Framework</mat-label>
<mat-select
[(ngModel)]="selectedFramework"
(selectionChange)="onChange($event)"
>
<mat-select-trigger>
<div class="flex-container">
<mat-icon svgIcon="{{ selectedFramework.svgIcon }}"></mat-icon
>{{ selectedFramework.label }}
</div>
</mat-select-trigger>
<mat-option *ngFor="let framework of frameworks" [value]="framework">
<div class="flex-container">
<mat-icon svgIcon="{{ framework.svgIcon }}"></mat-icon>
{{ framework.label }}
</div>
</mat-option>
</mat-select>
</mat-form-field>
<!-- ... -->
Update the store
As we need to make the user choice persistent between the two pages, we will update our NgRx store by creating a new Action
that will be dispatched when the user choose a framework in the landing page.
This Action updates the kind
value of the codegen
state which will tell us which framework the user has chosen.
If for some reason the user didn't choose a framework, we set kind
to 1 (corresponding to Angular
) by default.
export interface CodeGenSettings {
kind: CodeGenKind;
content?: XlayersNgxEditorModel[];
buttons?: XlayersExporterNavBar;
}
export class SelectCodegenKind {
static readonly type = '[CodeGen] Select Kind';
constructor(public kind: CodeGenKind) {}
}
@State<CodeGenSettings>({
name: 'codegen',
defaults: {
kind: 1,
},
})
@Injectable()
export class CodeGenState {
// ...
return codegen;
}
@Action(SelectCodegenKind)
selectKind(
{ setState, getState }: StateContext<CodeGenSettings>,
action: SelectCodegenKind
) {
const state = getState();
setState({
...state,
kind: action.kind,
});
}
Inside the landing page we will dispatch the SelectCodegenKind
action:
selectFramework(framework: CodeGenKind) {
this.store.dispatch(new SelectCodegenKind(framework));
this.router.navigateByUrl('/upload');
}
Inside the codegen generation page, we can subscribe to the codegen
from our store and generate the code for the selected codegen.kind
.
ngOnInit() {
this.store.select(CodeGenState.codegen).subscribe((codegen) => {
if (codegen.kind) {
this.selectedFramework = this.frameworks.find(
(framework) => framework.codegenType === codegen.kind
);
}
this.codeSetting = this.codegen.generate(codegen.kind);
});
}
Final result
This is the final user workflow.
Here is a small presentation of the final user workflow in xLayers.
Takeaway
Problems encountered
The simplest part of this contribution was the implementation of the solution.
The design of the user experience was a little more interesting and needed us to think way more than we thought.
What did I learn ?
As I said above, the richest part was the reflection around the user experience.
In addition, I was able to discover a codebase that I did not know and apply some concepts of NgRx.