Skip to content

Commit ca3f955

Browse files
committed
fix: retain text outside of variables intact
1 parent af5e58c commit ca3f955

File tree

6 files changed

+67
-32
lines changed

6 files changed

+67
-32
lines changed

libs/journeys/ui/src/components/Button/Button.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,7 @@ describe('Button', () => {
10671067
expect(screen.getByRole('button')).toHaveTextContent('{{ name }}')
10681068
})
10691069

1070-
it('returns original label when not a strict match', () => {
1070+
it('replaces custom fields within mixed strings and leaves other text intact', () => {
10711071
const journeyWithFields = {
10721072
journeyCustomizationFields: [
10731073
{
@@ -1093,7 +1093,7 @@ describe('Button', () => {
10931093
</MockedProvider>
10941094
)
10951095

1096-
expect(screen.getByRole('button')).toHaveTextContent('Hello {{ name }}!')
1096+
expect(screen.getByRole('button')).toHaveTextContent('Hello Alice!')
10971097
})
10981098

10991099
it('uses defaultValue when value is null', () => {

libs/journeys/ui/src/components/TextResponse/utils/getTextResponseValues/getTextResponseValues.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,25 @@ describe('getTextResponseValues', () => {
6666
it('resolves values for embed variant', () => {
6767
const input: TextResponseStrings = {
6868
label: ' {{ name }} ',
69-
placeholder: ' {{ title: CTO }} ',
69+
placeholder: '{{ title: CTO }}',
7070
hint: '{{ some: value }}'
7171
}
7272
const result = getTextResponseValues(input, fields, 'embed')
73-
expect(result.label).toBe('Alice')
73+
expect(result.label).toBe(' Alice ')
7474
expect(result.placeholder).toBe('Child of God')
7575
expect(result.hint).toBe('Some Value')
7676
})
7777

78-
it('returns original strings when not a strict pattern match', () => {
78+
it('replaces custom fields within mixed strings and leaves non-custom-field text intact', () => {
7979
const input: TextResponseStrings = {
8080
label: 'Hello {{ name }}!',
8181
placeholder: 'name',
8282
hint: '{{name}} extra'
8383
}
8484
const result = getTextResponseValues(input, fields, 'default')
85-
expect(result.label).toBe('Hello {{ name }}!')
85+
expect(result.label).toBe('Hello Alice!')
8686
expect(result.placeholder).toBe('name')
87-
expect(result.hint).toBe('{{name}} extra')
87+
expect(result.hint).toBe('Alice extra')
8888
})
8989

9090
it('handles null placeholder and hint', () => {

libs/journeys/ui/src/components/Typography/Typography.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ describe('Typography', () => {
134134
).toBeInTheDocument()
135135
})
136136

137-
it('returns original string when not a strict match', () => {
137+
it('replaces custom fields within mixed strings and leaves other text intact', () => {
138138
const journey = {
139139
journeyCustomizationFields: [
140140
{
@@ -157,7 +157,7 @@ describe('Typography', () => {
157157
)
158158

159159
expect(
160-
screen.getByRole('heading', { name: 'Hello {{ name }}!', level: 3 })
160+
screen.getByRole('heading', { name: 'Hello Alice!', level: 3 })
161161
).toBeInTheDocument()
162162
})
163163

libs/journeys/ui/src/libs/resolveJourneyCustomizationString/resolveJourneyCustomizationString.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ describe('resolveJourneyCustomizationString', () => {
1414
expect(resolveJourneyCustomizationString(null, fields)).toBeNull()
1515
})
1616

17-
it('returns the original string when it does not strictly match the pattern', () => {
17+
it('replaces custom fields within mixed strings and leaves non-token text intact', () => {
1818
expect(resolveJourneyCustomizationString('Hello {{ name }}!', fields)).toBe(
19-
'Hello {{ name }}!'
19+
'Hello Alice!'
2020
)
2121
expect(resolveJourneyCustomizationString('name', fields)).toBe('name')
2222
expect(resolveJourneyCustomizationString('{{name}} extra', fields)).toBe(
23-
'{{name}} extra'
23+
'Alice extra'
2424
)
2525
})
2626

@@ -60,13 +60,13 @@ describe('resolveJourneyCustomizationString', () => {
6060
)
6161
})
6262

63-
it('matches with leading/trailing whitespace around the entire string', () => {
63+
it('replaces custom fields regardless of surrounding whitespace', () => {
6464
expect(resolveJourneyCustomizationString(' {{ name }} ', fields)).toBe(
65-
'Alice'
65+
' Alice '
6666
)
6767
expect(
6868
resolveJourneyCustomizationString(' {{ title: Some Title }}', fields)
69-
).toBe('Child of God')
69+
).toBe(' Child of God')
7070
})
7171

7272
it('trims spaces around the key before lookup', () => {

libs/journeys/ui/src/libs/resolveJourneyCustomizationString/resolveJourneyCustomizationString.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,15 @@ export function resolveJourneyCustomizationString(
1313
): string | null {
1414
if (input == null) return null
1515

16-
const trimmed = input.trim()
16+
const customFieldsPattern =
17+
/\{\{\s*([^:}]+)(?:\s*:\s*(?:(['"])([^'"]*)\2|([^}]*?)))?\s*\}\}/g
1718

18-
const pattern =
19-
/^\{\{\s*([^:}]+)(?:\s*:\s*(?:(['"])([^'"]*)\2|([^}]*?)))?\s*\}\}$/
19+
const replaced = input.replace(customFieldsPattern, (fullMatch, key) => {
20+
const trimmedKey = String(key).trim()
21+
const field = journeyCustomizationFields.find((f) => f.key === trimmedKey)
22+
const resolved = field?.value ?? field?.defaultValue
23+
return resolved ?? fullMatch
24+
})
2025

21-
const match = trimmed.match(pattern)
22-
if (match == null) return input
23-
24-
const key = match[1].trim()
25-
const field = journeyCustomizationFields.find((f) => f.key === key)
26-
if (field == null) return input
27-
28-
const resolved = field.value ?? field.defaultValue
29-
return resolved ?? input
26+
return replaced
3027
}

libs/journeys/ui/src/libs/useGetValueFromJourneyCustomizationString/useGetValueFromJourneyCustomizationString.spec.tsx

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ describe('useGetValueFromJourneyCustomizationString', () => {
8383
expect(result.current).toBe('{{ unknown }}')
8484
})
8585

86-
it('trims and strictly matches the pattern', () => {
86+
it('replaces custom fields within mixed strings and leaves non-custom-field text intact', () => {
8787
const journey = {
8888
journeyCustomizationFields: [
8989
{
@@ -97,11 +97,49 @@ describe('useGetValueFromJourneyCustomizationString', () => {
9797
]
9898
} as unknown as Journey
9999

100+
const { result } = renderHook(
101+
({ label }: { label: string }) =>
102+
useGetValueFromJourneyCustomizationString(label),
103+
{
104+
initialProps: { label: 'Hello {{ name }}!' },
105+
wrapper: ({ children }) => (
106+
<JourneyProvider value={{ journey, variant: 'default' }}>
107+
{children}
108+
</JourneyProvider>
109+
)
110+
}
111+
)
112+
113+
expect(result.current).toBe('Hello Alice!')
114+
})
115+
116+
it('supports multiple custom fields in one string and repeated keys', () => {
117+
const journey = {
118+
journeyCustomizationFields: [
119+
{
120+
__typename: 'JourneyCustomizationField',
121+
id: '1',
122+
journeyId: 'journeyId',
123+
key: 'first',
124+
value: 'John',
125+
defaultValue: 'J'
126+
},
127+
{
128+
__typename: 'JourneyCustomizationField',
129+
id: '2',
130+
journeyId: 'journeyId',
131+
key: 'last',
132+
value: 'Doe',
133+
defaultValue: 'D'
134+
}
135+
]
136+
} as unknown as Journey
137+
100138
const { result, rerender } = renderHook(
101139
({ label }: { label: string }) =>
102140
useGetValueFromJourneyCustomizationString(label),
103141
{
104-
initialProps: { label: ' {{ name }} ' },
142+
initialProps: { label: 'Hello {{ first }} {{ last }}!' },
105143
wrapper: ({ children }) => (
106144
<JourneyProvider value={{ journey, variant: 'default' }}>
107145
{children}
@@ -110,9 +148,9 @@ describe('useGetValueFromJourneyCustomizationString', () => {
110148
}
111149
)
112150

113-
expect(result.current).toBe('Alice')
151+
expect(result.current).toBe('Hello John Doe!')
114152

115-
rerender({ label: 'Hello {{ name }}!' })
116-
expect(result.current).toBe('Hello {{ name }}!')
153+
rerender({ label: '{{ first }} & {{ first }}' })
154+
expect(result.current).toBe('John & John')
117155
})
118156
})

0 commit comments

Comments
 (0)