Fix data binding replacing partial matches
This commit is contained in:
parent
67786ea58a
commit
c9cf9ad506
|
@ -1317,6 +1317,22 @@ const shouldReplaceBinding = (currentValue, from, convertTo, binding) => {
|
||||||
return !invalids.find(invalid => noSpaces?.includes(invalid))
|
return !invalids.find(invalid => noSpaces?.includes(invalid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If converting readable to runtime we need to ensure we don't replace words
|
||||||
|
// which are substrings of other words - e.g. a binding of `a` would turn
|
||||||
|
// `hah` into `h[a]h` which is obviously wrong. To avoid this we can strip all
|
||||||
|
// expanded versions of the binding, then ensure the binding still exists.
|
||||||
|
const excludeExtensions = (string, binding) => {
|
||||||
|
// Regex to find prefixed bindings (e.g. exclude xfoo for foo)
|
||||||
|
const regex1 = new RegExp(`[a-zA-Z0-9-_]+${binding}[a-zA-Z0-9-_]*`, "g")
|
||||||
|
// Regex to find prefixed bindings (e.g. exclude foox for foo)
|
||||||
|
const regex2 = new RegExp(`[a-zA-Z0-9-_]*${binding}[a-zA-Z0-9-_]+`, "g")
|
||||||
|
const matches = [...string.matchAll(regex1), ...string.matchAll(regex2)]
|
||||||
|
for (let match of matches) {
|
||||||
|
string = string.replace(match[0], new Array(match[0].length + 1).join("*"))
|
||||||
|
}
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function which replaces a string between given indices.
|
* Utility function which replaces a string between given indices.
|
||||||
*/
|
*/
|
||||||
|
@ -1361,6 +1377,10 @@ const bindingReplacement = (
|
||||||
// in the search, working from longest to shortest so always use best match first
|
// in the search, working from longest to shortest so always use best match first
|
||||||
let searchString = newBoundValue
|
let searchString = newBoundValue
|
||||||
for (let from of convertFromProps) {
|
for (let from of convertFromProps) {
|
||||||
|
// Blank out all extensions of this string to avoid partial matches
|
||||||
|
if (convertTo === "runtimeBinding") {
|
||||||
|
searchString = excludeExtensions(searchString, from)
|
||||||
|
}
|
||||||
const binding = bindableProperties.find(el => el[convertFrom] === from)
|
const binding = bindableProperties.find(el => el[convertFrom] === from)
|
||||||
if (
|
if (
|
||||||
isJS ||
|
isJS ||
|
||||||
|
|
|
@ -72,6 +72,13 @@ describe("Builder dataBinding", () => {
|
||||||
runtimeBinding: "count",
|
runtimeBinding: "count",
|
||||||
type: "context",
|
type: "context",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
category: "Bindings",
|
||||||
|
icon: "Brackets",
|
||||||
|
readableBinding: "location",
|
||||||
|
runtimeBinding: "[location]",
|
||||||
|
type: "context",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
it("should convert a readable binding to a runtime one", () => {
|
it("should convert a readable binding to a runtime one", () => {
|
||||||
const textWithBindings = `Hello {{ Current User.firstName }}! The count is {{ Binding.count }}.`
|
const textWithBindings = `Hello {{ Current User.firstName }}! The count is {{ Binding.count }}.`
|
||||||
|
@ -83,6 +90,18 @@ describe("Builder dataBinding", () => {
|
||||||
)
|
)
|
||||||
).toEqual(`Hello {{ [user].[firstName] }}! The count is {{ count }}.`)
|
).toEqual(`Hello {{ [user].[firstName] }}! The count is {{ count }}.`)
|
||||||
})
|
})
|
||||||
|
it("should not convert a partial match", () => {
|
||||||
|
const textWithBindings = `location {{ _location Zlocation location locationZ _location_ }}`
|
||||||
|
expect(
|
||||||
|
readableToRuntimeBinding(
|
||||||
|
bindableProperties,
|
||||||
|
textWithBindings,
|
||||||
|
"runtimeBinding"
|
||||||
|
)
|
||||||
|
).toEqual(
|
||||||
|
`location {{ _location Zlocation [location] locationZ _location_ }}`
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("updateReferencesInObject", () => {
|
describe("updateReferencesInObject", () => {
|
||||||
|
|
Loading…
Reference in New Issue