further changes to support persisting values when component changes

This commit is contained in:
Peter Clement 2025-02-12 12:10:53 +00:00
parent deeb1fc29f
commit af0c722af1
6 changed files with 90 additions and 33 deletions

View File

@ -1,37 +1,71 @@
<script>
<script lang="ts">
import { onMount } from "svelte"
import { Input, Icon, Body, AbsTooltip } from "@budibase/bbui"
import { previewStore } from "@/stores/builder"
import {
Input,
Icon,
Body,
AbsTooltip,
TooltipPosition,
} from "@budibase/bbui"
import { previewStore, selectedScreen } from "@/stores/builder"
import { ComponentContext } from "@budibase/types"
export let baseRoute = ""
export let testValue = ""
$: routeParams = baseRoute.match(/:[a-zA-Z]+/g) || []
$: placeholder = (() => {
// Helper function to extract the route parameters
// e.g /employees/:id/:name becomes /employees/1/John for the placeholder
if (!routeParams.length) return "Add test values"
const segments = baseRoute.split("/").slice(2)
let paramCount = 1
let testValue: string | undefined
$: placeholder = getPlaceholder(baseRoute)
$: baseInput = createBaseInput(baseRoute)
$: updateTestValueFromContext($previewStore.selectedComponentContext)
$: if ($selectedScreen) {
testValue = ""
}
const getPlaceholder = (route: string) => {
const trimmed = route.replace(/\/$/, "")
if (trimmed.startsWith("/:")) {
return "1"
}
const segments = trimmed.split("/").slice(2)
let count = 1
return segments
.map(segment => {
if (segment.startsWith(":")) {
return paramCount++
}
return segment
})
.map(segment => (segment.startsWith(":") ? count++ : segment))
.join("/")
})()
}
$: {
if ($previewStore.selectedComponentContext?.url?.testValue !== undefined) {
testValue = $previewStore.selectedComponentContext.url.testValue
// This function is needed to repopulate the test value from componentContext
// when a user navigates to another component and then back again
const updateTestValueFromContext = (context: ComponentContext | null) => {
if (context?.url && !testValue) {
const { wild, ...urlParams } = context.url
const queryParams = context.query
if (Object.values(urlParams).some(v => Boolean(v))) {
let value = baseRoute
.split("/")
.slice(2)
.map(segment =>
segment.startsWith(":")
? urlParams[segment.slice(1)] || ""
: segment
)
.join("/")
const qs = new URLSearchParams(queryParams).toString()
if (qs) {
value += `?${qs}`
}
testValue = value
}
}
}
const onVariableChange = e => {
const createBaseInput = (baseRoute: string) => {
return baseRoute === "/" || baseRoute.split("/")[1]?.startsWith(":")
? "/"
: `/${baseRoute.split("/")[1]}/`
}
const onVariableChange = (e: CustomEvent) => {
previewStore.updateUrl({ route: baseRoute, testValue: e.detail })
previewStore.requestComponentContext()
}
onMount(() => {
@ -43,8 +77,8 @@
<div class="info">
<Body size="XS">URL Variable Testing</Body>
<AbsTooltip
text="Test how your screen behaves with different URL parameters. Enter values in the format shown in the placeholder."
position={"bottom"}
text="Test how your screen behaves with different URL parameters. Enter values in the format shown in the placeholder below."
position={TooltipPosition.Top}
noWrap
>
<div class="icon">
@ -54,7 +88,7 @@
</div>
<div class="url-test-container">
<div class="base-input">
<Input disabled={true} value={`/${baseRoute.split("/")[1]}/`} />
<Input disabled={true} value={baseInput} />
</div>
<div class="variable-input">
<Input

View File

@ -19,7 +19,6 @@
$: bindings = getBindableProperties($selectedScreen, null)
$: screenSettings = getScreenSettings($selectedScreen)
let urlTestValue = ""
let errors = {}
@ -100,7 +99,6 @@
control: URLVariableTestInput,
props: {
baseRoute: screen.routing?.route,
testValue: urlTestValue,
},
},
]

View File

@ -1,9 +1,8 @@
import { get } from "svelte/store"
import { BudiStore } from "../BudiStore"
import { PreviewDevice, ComponentContext } from "@budibase/types"
type PreviewDevice = "desktop" | "tablet" | "mobile"
type PreviewEventHandler = (name: string, payload?: any) => void
type ComponentContext = Record<string, any>
interface PreviewState {
previewDevice: PreviewDevice

View File

@ -9,6 +9,7 @@ import {
componentStore,
navigationStore,
selectedComponent,
previewStore,
} from "@/stores/builder"
import { createHistoryStore, HistoryStore } from "@/stores/builder/history"
import { API } from "@/api"
@ -110,6 +111,9 @@ export class ScreenStore extends BudiStore<ScreenState> {
return
}
// When we select a screen, we want to clear the url binding test value
previewStore.updateUrl({ route: screen.routing.route, testValue: "" })
// Select new screen
this.update(state => {
state.selectedScreenId = screen._id

View File

@ -120,15 +120,36 @@ const createRouteStore = () => {
return `${base}#${relativeURL}`
}
const setTestUrlParams = (route: string, testValue: string) => {
const routeSegments = route.split("/").slice(2)
const testSegments = testValue.split("/")
if (route === "/") {
return
}
const [pathPart, queryPart] = testValue.split("?")
const routeSegments = route.split("/").filter(Boolean)
// If first segment is a parameter (e.g. /:foo), include it in processing
const startIndex = routeSegments[0]?.startsWith(":") ? 0 : 1
const segments = routeSegments.slice(startIndex)
const testSegments = pathPart.split("/")
const params: Record<string, string> = {}
routeSegments.forEach((segment, index) => {
segments.forEach((segment, index) => {
if (segment.startsWith(":") && index < testSegments.length) {
params[segment.slice(1)] = testSegments[index]
}
})
const queryParams: Record<string, string> = {}
if (queryPart) {
queryPart.split("&").forEach(param => {
const [key, value] = param.split("=")
if (key && value) {
queryParams[key] = value
}
})
}
setQueryParams({ ...queryParams })
store.update(state => ({ ...state, testUrlParams: params }))
}
return {

View File

@ -1 +1,2 @@
export type PreviewDevice = "desktop" | "tablet" | "mobile"
export type ComponentContext = Record<string, any>