Refactored tours. Tours will end if a TourWrap is removed from screen.
This commit is contained in:
parent
5c5dc4c155
commit
b0cd3d4d03
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Popover, Layout, Heading, Body, Button, Link } from "@budibase/bbui"
|
||||
import { TOURS } from "./tours.js"
|
||||
import { TOURS, getCurrentStepIdx } from "./tours.js"
|
||||
import { goto, layout, isActive } from "@roxi/routify"
|
||||
import { builderStore } from "stores/builder"
|
||||
|
||||
|
@ -20,6 +20,12 @@
|
|||
|
||||
const updateTourStep = (targetStepKey, tourKey) => {
|
||||
if (!tourKey) {
|
||||
tourSteps = null
|
||||
tourStepIdx = null
|
||||
lastStep = null
|
||||
tourStep = null
|
||||
popoverAnchor = null
|
||||
popover = null
|
||||
return
|
||||
}
|
||||
if (!tourSteps?.length) {
|
||||
|
@ -78,16 +84,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getCurrentStepIdx = (steps, tourStepKey) => {
|
||||
if (!steps?.length) {
|
||||
return
|
||||
}
|
||||
if (steps?.length && !tourStepKey) {
|
||||
return 0
|
||||
}
|
||||
return steps.findIndex(step => step.id === tourStepKey)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if tourKey}
|
||||
|
@ -100,6 +96,7 @@
|
|||
dismissible={false}
|
||||
offset={15}
|
||||
handlePostionUpdate={tourStep?.positionHandler}
|
||||
customZindex={3}
|
||||
>
|
||||
<div class="tour-content">
|
||||
<Layout noPadding gap="M">
|
||||
|
|
|
@ -1,44 +1,62 @@
|
|||
<script>
|
||||
import { tourHandler } from "./tourHandler"
|
||||
import { TOURSBYSTEP } from "./tours"
|
||||
import { TOURSBYSTEP, TOURS, getCurrentStepIdx } from "./tours"
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { builderStore } from "stores/builder"
|
||||
|
||||
export let stepKeys = []
|
||||
|
||||
let ready = false
|
||||
let handler
|
||||
let registered = []
|
||||
let registered = {}
|
||||
|
||||
const registerTourNode = (tourKey, stepKey) => {
|
||||
const step = TOURSBYSTEP[stepKey]
|
||||
if (
|
||||
ready &&
|
||||
step &&
|
||||
!registered.includes(stepKey) &&
|
||||
step?.tour === tourKey
|
||||
) {
|
||||
if (ready && step && !registered[stepKey] && step?.tour === tourKey) {
|
||||
const elem = document.querySelector(step.query)
|
||||
handler = tourHandler(elem, stepKey)
|
||||
registered.push(stepKey)
|
||||
registered[stepKey] = tourHandler(elem, stepKey)
|
||||
}
|
||||
}
|
||||
|
||||
$: tourKeyWatch = $builderStore.tourKey
|
||||
$: tourStepKeyWatch = $builderStore.tourStepKey
|
||||
$: if (tourKeyWatch || stepKeys || ready) {
|
||||
stepKeys.forEach(tourStepKey => {
|
||||
registerTourNode(tourKeyWatch, tourStepKey)
|
||||
})
|
||||
}
|
||||
|
||||
$: if (tourKeyWatch || tourStepKeyWatch) {
|
||||
let tourStepIdx = getCurrentStepIdx(
|
||||
TOURS[tourKeyWatch]?.steps,
|
||||
tourStepKeyWatch
|
||||
)
|
||||
let currentStep = TOURS[tourKeyWatch]?.steps?.[tourStepIdx]
|
||||
if (currentStep?.scrollIntoView) {
|
||||
let currentNode = $builderStore.tourNodes?.[currentStep.id]
|
||||
if (currentNode) {
|
||||
currentNode.scrollIntoView({ behavior: "smooth", block: "center" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
ready = true
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (handler) {
|
||||
Object.entries(registered).forEach(entry => {
|
||||
const handler = entry[1]
|
||||
const stepKey = entry[0]
|
||||
// Run step destroy, de-register nodes in the builderStore and local cache
|
||||
handler.destroy()
|
||||
}
|
||||
delete registered[stepKey]
|
||||
|
||||
// Check if the step is part of an active tour. End the tour if that is the case
|
||||
const step = TOURSBYSTEP[stepKey]
|
||||
if (step.tour === tourKeyWatch) {
|
||||
builderStore.setTour()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -32,14 +32,18 @@ export const TOUR_KEYS = {
|
|||
BUILDER_FORM_VIEW_UPDATE: "builder-form-view-update",
|
||||
}
|
||||
|
||||
export const getCurrentStepIdx = (steps, tourStepKey) => {
|
||||
if (!steps?.length) {
|
||||
return
|
||||
}
|
||||
if (steps?.length && !tourStepKey) {
|
||||
return 0
|
||||
}
|
||||
return steps.findIndex(step => step.id === tourStepKey)
|
||||
}
|
||||
|
||||
const resetTourState = () => {
|
||||
builderStore.update(state => ({
|
||||
...state,
|
||||
tourNodes: undefined,
|
||||
tourKey: undefined,
|
||||
tourKeyStep: undefined,
|
||||
onboarding: false,
|
||||
}))
|
||||
builderStore.setTour()
|
||||
}
|
||||
|
||||
const endUserOnboarding = async ({ skipped = false } = {}) => {
|
||||
|
@ -58,6 +62,7 @@ const endUserOnboarding = async ({ skipped = false } = {}) => {
|
|||
// Update the cached user
|
||||
await auth.getSelf()
|
||||
|
||||
builderStore.endBuilderOnboarding()
|
||||
resetTourState()
|
||||
} catch (e) {
|
||||
console.error("Onboarding failed", e)
|
||||
|
@ -222,6 +227,7 @@ const getTours = () => {
|
|||
},
|
||||
positionHandler: customPositionHandler,
|
||||
align: "left-outside",
|
||||
scrollIntoView: true,
|
||||
},
|
||||
],
|
||||
onSkip: async () => {
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
const release_date = new Date("2023-03-01T00:00:00.000Z")
|
||||
const onboarded = new Date($auth.user?.onboardedAt)
|
||||
if (onboarded < release_date) {
|
||||
builderStore.startTour(TOUR_KEYS.FEATURE_ONBOARDING)
|
||||
builderStore.setTour(TOUR_KEYS.FEATURE_ONBOARDING)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@
|
|||
|
||||
// Handler for Datasource Screen Creation
|
||||
const completeDatasourceScreenCreation = async () => {
|
||||
templates = rowListScreen(selectedDatasources)
|
||||
templates = rowListScreen(selectedDatasources, mode)
|
||||
|
||||
const screens = templates.map(template => {
|
||||
let screenTemplate = template.create()
|
||||
|
@ -192,10 +192,17 @@
|
|||
}
|
||||
|
||||
const loadNewScreen = createdScreens => {
|
||||
const lastScreen = createdScreens.slice(-1)
|
||||
const lastScreen = createdScreens.slice(-1)[0]
|
||||
|
||||
// Go to new screen
|
||||
$goto(`./${lastScreen._id}`)
|
||||
if (lastScreen?.props?._children.length) {
|
||||
// Focus on the main component for the streen type
|
||||
const mainComponent = lastScreen?.props?._children?.[0]._id
|
||||
$goto(`./${lastScreen._id}/${mainComponent}`)
|
||||
} else {
|
||||
$goto(`./${lastScreen._id}`)
|
||||
}
|
||||
|
||||
screenStore.select(lastScreen._id)
|
||||
}
|
||||
|
||||
|
@ -206,8 +213,6 @@
|
|||
return screenTemplate
|
||||
})
|
||||
const createdScreens = await createScreens({ screens, screenAccessRole })
|
||||
const lastScreen = createdScreens?.slice(-1)?.pop()
|
||||
const mainComponent = lastScreen?.props?._children?.[0]._id
|
||||
|
||||
if (formType === "Update" || formType === "Create") {
|
||||
const associatedTour =
|
||||
|
@ -217,18 +222,12 @@
|
|||
|
||||
const tourRequired = !$auth?.user?.tours?.[associatedTour]
|
||||
if (tourRequired) {
|
||||
builderStore.update(state => ({
|
||||
...state,
|
||||
tourStepKey: null,
|
||||
tourNodes: null,
|
||||
tourKey: associatedTour,
|
||||
}))
|
||||
builderStore.setTour(associatedTour)
|
||||
}
|
||||
}
|
||||
|
||||
// Go to new screen
|
||||
$goto(`./${lastScreen._id}/${mainComponent}`)
|
||||
screenStore.select(lastScreen._id)
|
||||
loadNewScreen(createdScreens)
|
||||
}
|
||||
|
||||
// Submit screen config for creation.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import blankImage from "./images/blank.png"
|
||||
import tableImage from "./images/table.png"
|
||||
import gridImage from "./images/grid.png"
|
||||
import formImage from "./images/form.png" //optimized example
|
||||
import formImage from "./images/form.png"
|
||||
import CreateScreenModal from "./CreateScreenModal.svelte"
|
||||
import { screenStore } from "stores/builder"
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { TOUR_KEYS } from "components/portal/onboarding/tours.js"
|
|||
|
||||
export const INITIAL_BUILDER_STATE = {
|
||||
previousTopNavPath: {},
|
||||
highlightedSettingKey: null,
|
||||
highlightedSetting: null,
|
||||
propertyFocus: null,
|
||||
builderSidePanel: false,
|
||||
onboarding: false,
|
||||
|
@ -61,7 +61,7 @@ export class BuilderStore extends BudiStore {
|
|||
highlightSetting(key, type) {
|
||||
this.update(state => ({
|
||||
...state,
|
||||
highlightedSetting: { key, type: type || "info" },
|
||||
highlightedSetting: key ? { key, type: type || "info" } : null,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -135,9 +135,18 @@ export class BuilderStore extends BudiStore {
|
|||
}))
|
||||
}
|
||||
|
||||
startTour(tourKey) {
|
||||
endBuilderOnboarding() {
|
||||
this.update(state => ({
|
||||
...state,
|
||||
onboarding: false,
|
||||
}))
|
||||
}
|
||||
|
||||
setTour(tourKey) {
|
||||
this.update(state => ({
|
||||
...state,
|
||||
tourStepKey: null,
|
||||
tourNodes: null,
|
||||
tourKey: tourKey,
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -88,14 +88,42 @@ describe("Builder store", () => {
|
|||
)
|
||||
})
|
||||
|
||||
it("Sync a highlighted setting key to state", ctx => {
|
||||
expect(ctx.test.store.highlightedSettingKey).toBeNull()
|
||||
it("Sync a highlighted setting key to state. Default to info type", ctx => {
|
||||
expect(ctx.test.store.highlightedSetting).toBeNull()
|
||||
|
||||
ctx.test.builderStore.highlightSetting("testing")
|
||||
|
||||
expect(ctx.test.store).toStrictEqual({
|
||||
...INITIAL_BUILDER_STATE,
|
||||
highlightedSettingKey: "testing",
|
||||
highlightedSetting: {
|
||||
key: "testing",
|
||||
type: "info",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("Sync a highlighted setting key to state. Use provided type", ctx => {
|
||||
expect(ctx.test.store.highlightedSetting).toBeNull()
|
||||
|
||||
ctx.test.builderStore.highlightSetting("testing", "error")
|
||||
|
||||
expect(ctx.test.store).toStrictEqual({
|
||||
...INITIAL_BUILDER_STATE,
|
||||
highlightedSetting: {
|
||||
key: "testing",
|
||||
type: "error",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("Sync a highlighted setting key to state. Unset when no value is passed", ctx => {
|
||||
expect(ctx.test.store.highlightedSetting).toBeNull()
|
||||
|
||||
ctx.test.builderStore.highlightSetting("testing", "error")
|
||||
ctx.test.builderStore.highlightSetting()
|
||||
|
||||
expect(ctx.test.store).toStrictEqual({
|
||||
...INITIAL_BUILDER_STATE,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue