feat(frontend): add driverjs (#2327)

* feat(frontend): add driverjs

* feat(frontend): pipeline done

* feat(frontend): texts updated

* feat(frontend): split tutorials into two

* feat(frontend): Branch one tutorial

* wip

* feat(frontend): flow editor tutorial v0 done

* feat(frontend): flow editor tutorial v0 done

* feat(frontend): fix how tutorials are started

* feat(frontend): fix how tutorials are started
This commit is contained in:
Faton Ramadani
2023-10-04 10:12:07 +02:00
committed by GitHub
parent 8319f5d0e5
commit 33b1c752ae
29 changed files with 1800 additions and 297 deletions
@@ -0,0 +1,415 @@
<script lang="ts">
import { driver } from 'driver.js'
import 'driver.js/dist/driver.css'
import { createEventDispatcher, getContext, tick } from 'svelte'
import type { FlowEditorContext } from '../flows/types'
import TutorialItem from './TutorialItem.svelte'
import { emptyFlowModuleState } from '../flows/utils'
import {
clickButtonBySelector,
setInputBySelector,
triggerAddFlowStep,
selectFlowStepKind,
selectOptionsBySelector
} from './utils'
import { updateProgress } from '$lib/tutorialUtils'
export let shouldRenderButton: boolean = true
const { flowStore, selectedId, flowStateStore } =
getContext<FlowEditorContext>('FlowEditorContext')
const dispatch = createEventDispatcher()
export function runTutorial() {
const forloopTutorial = driver({
showProgress: true,
allowClose: true,
onPopoverRender: (popover, { config, state }) => {
if (state.activeIndex == 0) {
const skipThisButton = document.createElement('button')
skipThisButton.innerText = 'Skip this'
skipThisButton.addEventListener('click', () => {
updateProgress(1)
forloopTutorial.destroy()
})
skipThisButton.setAttribute(
'class',
'border px-2 py-1 !text-xs font-normal rounded-md hover:bg-surface-hover transition-all flex items-center'
)
const skipAllButton = document.createElement('button')
skipAllButton.innerText = 'Skip all'
skipAllButton.addEventListener('click', () => {
dispatch('skipAll')
forloopTutorial.destroy()
})
skipAllButton.setAttribute(
'class',
'border px-2 py-1 !text-xs font-normal rounded-md hover:bg-surface-hover transition-all flex items-center'
)
const popoverDescription = document.querySelector('#driver-popover-description')
const div = document.createElement('div')
div.setAttribute('class', 'flex flex-row gap-2')
if (popoverDescription) {
div.appendChild(skipThisButton)
div.appendChild(skipAllButton)
popoverDescription.appendChild(div)
}
}
},
steps: [
{
popover: {
title: 'Welcome in the Windmil Flow editor',
description: 'Learn how to build our first for loop to iterate on'
}
},
{
popover: {
title: 'Flows inputs',
description: 'Flows have inputs that can be used in the flow',
onNextClick: () => {
clickButtonBySelector('#flow-editor-virtual-Input')
setTimeout(() => {
forloopTutorial.moveNext()
})
}
},
element: '#svelvet-Input'
},
{
element: '#flow-editor-add-property',
popover: {
title: 'Add a property',
description: 'Click here to add a property to your schema',
onNextClick: () => {
clickButtonBySelector('#flow-editor-add-property')
setTimeout(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '#schema-modal-name',
popover: {
title: 'Name your property',
description: 'Give a name to your property. Here we will call it firstname',
onNextClick: () => {
setInputBySelector('#schema-modal-name', 'array')
forloopTutorial.moveNext()
}
}
},
{
element: '#schema-modal-type-array',
popover: {
title: 'Property type',
description: 'Choose the type of your property. Here we will choose array',
onNextClick: () => {
clickButtonBySelector('#schema-modal-type-array')
forloopTutorial.moveNext()
}
}
},
{
element: '#array-type-narrowing',
popover: {
title: 'Array type narrowing',
description: 'You can narrow the type of your array. Here we will choose numbers',
onNextClick: () => {
selectOptionsBySelector('#array-type-narrowing', 'number')
forloopTutorial.moveNext()
}
}
},
{
element: '#schema-modal-save',
popover: {
title: 'Save your property',
description: 'Click here to save your property',
onNextClick: () => {
clickButtonBySelector('#schema-modal-save')
setTimeout(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '#flow-editor-add-step-0',
popover: {
title: 'Add a step',
description: 'Click here to add a step to your flow',
onNextClick: () => {
triggerAddFlowStep(0)
setTimeout(() => {
forloopTutorial.moveNext()
})
}
}
},
{
popover: {
title: 'Steps kind',
description: "Choose the kind of step you want to add. Let's start with a simple action"
},
element: '#flow-editor-insert-module'
},
{
popover: {
title: 'Insert loop',
description: "Let's pick forloop",
onNextClick: () => {
selectFlowStepKind(4)
setTimeout(() => {
forloopTutorial.moveNext()
})
}
},
element: '#flow-editor-insert-module > div > button:nth-child(4)'
},
{
element: '#flow-editor-iterator-expression',
popover: {
title: 'Iterator expression',
description:
'The iterator expression is a javascript expression that respresents the array to iterate on. Here we will iterate on the firstname input letter by letter',
onNextClick: () => {
if ($flowStore.value.modules[0].value.type === 'forloopflow') {
if ($flowStore.value.modules[0].value.iterator.type === 'javascript') {
$flowStore.value.modules[0].value.iterator.expr = 'flow_input.array'
}
}
$flowStore = $flowStore
dispatch('reload')
tick().then(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '#flow-editor-iterator-expression',
popover: {
title: 'Iterator expression',
description:
'We can refer to the result of previous steps using the results object: results.a',
onNextClick: () => {
if ($flowStore.value.modules[0].value.type === 'forloopflow') {
$flowStore.value.modules[0].value.modules = [
{
id: 'b',
value: {
type: 'rawscript',
content: 'def main(x: int):\n return x*2',
// @ts-ignore
language: 'python3',
input_transforms: {
x: {
type: 'javascript',
// @ts-ignore
value: '',
expr: ''
}
}
}
}
]
}
$flowStateStore['b'] = emptyFlowModuleState()
$flowStateStore['b'].schema.properties = {
x: {
type: 'number',
description: '',
default: null
}
}
$flowStore = $flowStore
dispatch('reload')
tick().then(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '#svelvet-c',
popover: {
title: 'Step of the loop',
description: 'We added an action to the loop. Let’s configure it',
onNextClick: () => {
$selectedId = 'b'
$flowStore = $flowStore
dispatch('reload')
tick().then(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '#flow-editor-editor',
popover: {
title: 'Python step',
description:
'We can write python code in the editor. In this example we will capitalize the letter'
}
},
{
element: '#flow-editor-step-input',
popover: {
title: 'flow editor',
description: 'Description'
}
},
{
element: '#flow-editor-plug',
popover: {
title: 'Input configuration',
description:
'UI is autogenerated from your code. Let’s connect the input to the letter input',
onNextClick: () => {
clickButtonBySelector('#flow-editor-plug')
setTimeout(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '.key',
popover: {
title: 'Connect',
description: 'As we did before, we can connect to the iterator of the loop',
onNextClick: () => {
if (
$flowStore.value.modules[0].value.type === 'forloopflow' &&
$flowStore.value.modules[0].value.modules[0].value.type === 'rawscript'
) {
$flowStore.value.modules[0].value.modules[0].value.input_transforms = {
x: {
type: 'javascript',
expr: 'flow_input.iter.value'
}
}
}
$flowStore = $flowStore
dispatch('reload')
tick().then(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '#flow-editor-step-input',
popover: {
title: 'Iterator',
description:
'Loops expose an iterator object that contains the current value of the loop and the index'
}
},
{
element: '#flow-editor-test-flow',
popover: {
title: 'Test your flow',
description: 'We can now test our flow',
onNextClick: () => {
clickButtonBySelector('#flow-editor-test-flow')
setTimeout(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: 'arg-input-add-item',
popover: {
title: 'Flow input',
description: 'Let’s add an item to our array',
onNextClick: () => {
clickButtonBySelector('#arg-input-add-item')
setTimeout(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: 'arg-input-add-item',
popover: {
title: 'Flow input',
description: 'We can set the value of the item',
onNextClick: () => {
setInputBySelector('#arg-input-number-array', '25')
setTimeout(() => {
forloopTutorial.moveNext()
})
}
}
},
{
element: '#flow-editor-test-flow-drawer',
popover: {
title: 'Test your flow',
description: 'Finally we can test our flow, and view the results!',
onNextClick: () => {
clickButtonBySelector('#flow-editor-test-flow-drawer')
setTimeout(() => {
forloopTutorial.moveNext()
updateProgress(1)
})
}
}
}
]
})
forloopTutorial.drive()
}
</script>
{#if shouldRenderButton}
<TutorialItem
on:click={() => runTutorial()}
label="For loops tutorial"
index={1}
id="flow-builder-tutorial-forloops"
/>
{/if}