Skip to content

Commit f08179e

Browse files
committed
fix(landing): remove obsolete tracking and preserve preview interactions
1 parent 9d608e1 commit f08179e

13 files changed

Lines changed: 124 additions & 158 deletions

File tree

‎apps/sim/app/(landing)/components/hero/components/hero-platform-loop/production-workflow-stage.tsx‎

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -295,20 +295,22 @@ function PreviewActionBar({ block, running, workflowRunning, onRunToggle }: Prev
295295
{inertActions.map(({ label, Icon }) => (
296296
<Tooltip.Root key={label} preferAbove>
297297
<Tooltip.Trigger asChild>
298-
<Button
299-
type='button'
300-
variant='ghost'
301-
disabled
302-
aria-label={`${label} unavailable in preview`}
303-
className={cn(
304-
ACTION_BUTTON_STYLES,
305-
label === 'Delete' && LAST_ACTION_STYLES,
306-
workflowRunning && !running && BYSTANDER_ACTION_STYLES,
307-
sweeping && SWEEP_SLOT_STYLES
308-
)}
309-
>
310-
<Icon className='size-[14px]' />
311-
</Button>
298+
<span className='inline-flex'>
299+
<Button
300+
type='button'
301+
variant='ghost'
302+
disabled
303+
aria-label={`${label} unavailable in preview`}
304+
className={cn(
305+
ACTION_BUTTON_STYLES,
306+
label === 'Delete' && LAST_ACTION_STYLES,
307+
workflowRunning && !running && BYSTANDER_ACTION_STYLES,
308+
sweeping && SWEEP_SLOT_STYLES
309+
)}
310+
>
311+
<Icon className='size-[14px]' />
312+
</Button>
313+
</span>
312314
</Tooltip.Trigger>
313315
{!workflowRunning && <Tooltip.Content side='top'>{label}</Tooltip.Content>}
314316
</Tooltip.Root>

‎apps/sim/app/(landing)/cookie-policy/cookie-policy-content.tsx‎

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const COOKIE_POLICY_CONFIG: LegalPageConfig = {
4444
title: 'Cookie Policy',
4545
description:
4646
'What cookies Sim sets, why, how long they last, and how to change your choice at any time.',
47-
lastUpdated: 'September 3, 2026',
47+
lastUpdated: 'September 17, 2026',
4848
intro: [
4949
{
5050
kind: 'paragraph',
@@ -165,7 +165,7 @@ export const COOKIE_POLICY_CONFIG: LegalPageConfig = {
165165
[
166166
'__cf_bm',
167167
'Cloudflare',
168-
'Bot-management check on requests to providers we load, such as HubSpot and X.',
168+
'Bot-management check on requests to providers we load, such as X.',
169169
'30 minutes',
170170
],
171171
]),
@@ -177,10 +177,6 @@ export const COOKIE_POLICY_CONFIG: LegalPageConfig = {
177177
'Holds the session state for a specific Analytics property.',
178178
'13 months',
179179
],
180-
['__hstc', 'HubSpot', 'Tracks visits across sessions for the main tracker.', '6 months'],
181-
['hubspotutk', 'HubSpot', 'Identifies a visitor across form submissions.', '6 months'],
182-
['__hssc', 'HubSpot', 'Tracks the current session.', '30 minutes'],
183-
['__hssrc', 'HubSpot', 'Detects whether the visitor restarted their browser.', 'Session'],
184180
[
185181
'ph_*_posthog',
186182
'PostHog',
@@ -269,9 +265,8 @@ export const COOKIE_POLICY_CONFIG: LegalPageConfig = {
269265
Google Analytics
270266
</ProseLink>
271267
, <ProseLink href='https://adssettings.google.com'>Google Ads</ProseLink>,{' '}
272-
<ProseLink href='https://x.com/settings/privacy_and_safety'>X (Twitter)</ProseLink>,{' '}
273-
<ProseLink href='https://legal.hubspot.com/privacy-policy'>HubSpot</ProseLink>, and{' '}
274-
<ProseLink href='https://posthog.com/privacy'>PostHog</ProseLink>.
268+
<ProseLink href='https://x.com/settings/privacy_and_safety'>X (Twitter)</ProseLink>,
269+
and <ProseLink href='https://posthog.com/privacy'>PostHog</ProseLink>.
275270
</>
276271
),
277272
},
@@ -292,7 +287,6 @@ export const COOKIE_POLICY_CONFIG: LegalPageConfig = {
292287
The providers currently in use are{' '}
293288
<ProseLink href='https://policies.google.com/technologies/cookies'>Google</ProseLink>{' '}
294289
(Analytics and Ads),{' '}
295-
<ProseLink href='https://legal.hubspot.com/privacy-policy'>HubSpot</ProseLink>,{' '}
296290
<ProseLink href='https://x.com/en/privacy'>X (Twitter)</ProseLink>,{' '}
297291
<ProseLink href='https://ahrefs.com/legal/privacy-policy'>Ahrefs</ProseLink>,{' '}
298292
<ProseLink href='https://posthog.com/privacy'>PostHog</ProseLink>, and{' '}

‎apps/sim/app/(landing)/hubspot-page-view-tracker.test.tsx‎

Lines changed: 0 additions & 50 deletions
This file was deleted.

‎apps/sim/app/(landing)/hubspot-page-view-tracker.tsx‎

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
'use client'
22

33
import { useConsentScript } from '@c15t/nextjs/headless'
4-
import { HUBSPOT_SCRIPT, X_PIXEL_SCRIPT } from '@/lib/consent/scripts'
5-
import { HubspotPageViewTracker } from '@/app/(landing)/hubspot-page-view-tracker'
4+
import { X_PIXEL_SCRIPT } from '@/lib/consent/scripts'
65
import { XPageViewTracker } from '@/app/(landing)/x-page-view-tracker'
76

87
export function LandingConsentTracking() {
9-
const hubspot = useConsentScript({ script: HUBSPOT_SCRIPT, unmountBehavior: 'keep' })
108
const xPixel = useConsentScript({ script: X_PIXEL_SCRIPT, unmountBehavior: 'keep' })
119

12-
return (
13-
<>
14-
{hubspot.status === 'ready' && <HubspotPageViewTracker />}
15-
{xPixel.status === 'ready' && <XPageViewTracker />}
16-
</>
17-
)
10+
return xPixel.status === 'ready' ? <XPageViewTracker /> : null
1811
}

‎apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ export function ResourceTabs({
481481
if (isMultiDrag) {
482482
e.dataTransfer.effectAllowed = 'copy'
483483
e.dataTransfer.setData(SIM_RESOURCES_DRAG_TYPE, JSON.stringify(selected))
484-
const dragImage = buildMultiDragImage(e.currentTarget.closest('[role="tablist"]'), selected)
484+
const dragImage = buildMultiDragImage(e.currentTarget.closest('[data-tab-strip]'), selected)
485485
if (dragImage) {
486486
e.dataTransfer.setDragImage(dragImage, 16, 16)
487487
dragImageRef.current = dragImage

‎apps/sim/lib/consent/scripts.test.ts‎

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
GLOBAL_CONSENT_SCRIPTS,
88
GOOGLE_ADS_ID,
99
GOOGLE_ANALYTICS_ID,
10-
HUBSPOT_SCRIPT,
1110
X_PIXEL_SCRIPT,
1211
} from '@/lib/consent/scripts'
1312

@@ -27,7 +26,6 @@ const CALLBACK_INFO: ConsentScriptCallbackInfo = {
2726
afterEach(() => {
2827
window.dataLayer = []
2928
window.gtag = undefined
30-
window._hsq = []
3129
window.history.replaceState({}, '', '/')
3230
})
3331

@@ -49,8 +47,7 @@ describe('consent scripts', () => {
4947
])
5048
})
5149

52-
it('keeps landing vendors in separate consent categories', () => {
53-
expect(HUBSPOT_SCRIPT).toMatchObject({ id: 'hubspot', category: 'measurement' })
50+
it('gates the landing conversion pixel on marketing consent', () => {
5451
expect(X_PIXEL_SCRIPT).toMatchObject({
5552
id: 'x-pixel',
5653
category: 'marketing',
@@ -86,13 +83,4 @@ describe('consent scripts', () => {
8683
`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_ADS_ID}`
8784
)
8885
})
89-
90-
it('gives HubSpot a query-free path before its automatic first page view', () => {
91-
window.history.replaceState({}, '', '/demo?email=private@example.com#booking')
92-
window._hsq = []
93-
94-
HUBSPOT_SCRIPT.onBeforeLoad()
95-
96-
expect(window._hsq).toEqual([['setPath', '/demo']])
97-
})
9886
})

‎apps/sim/lib/consent/scripts.ts‎

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ export const X_DEMO_BOOKED_EVENT_ID = 'tw-q5xbl-q5xbn' as const
2323

2424
const AHREFS_ANALYTICS_KEY = 'WJ9yWTBAiQKZAE/2TyU/yA' as const
2525

26-
declare global {
27-
interface Window {
28-
_hsq?: unknown[][]
29-
}
30-
}
31-
3226
const GOOGLE_ANALYTICS_SCRIPT = gtag({
3327
id: GOOGLE_ANALYTICS_ID,
3428
category: 'measurement',
@@ -70,15 +64,3 @@ export const GLOBAL_CONSENT_SCRIPTS = [
7064

7165
/** Marketing-page integrations that should not load on a direct workspace visit. */
7266
export const X_PIXEL_SCRIPT = xPixel({ pixelId: X_PIXEL_ID })
73-
74-
/** HubSpot has no first-party c15t helper, so it uses the generic script contract. */
75-
export const HUBSPOT_SCRIPT = {
76-
id: 'hubspot',
77-
src: 'https://js-na2.hs-scripts.com/246720681.js',
78-
category: 'measurement',
79-
async: true,
80-
onBeforeLoad: () => {
81-
window._hsq ||= []
82-
window._hsq.push(['setPath', window.location.pathname])
83-
},
84-
} as const

‎apps/sim/lib/core/security/csp.ts‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,6 @@ const STATIC_SCRIPT_SRC = [
8383
'https://www.googleadservices.com',
8484
'https://googleads.g.doubleclick.net',
8585
'https://analytics.ahrefs.com',
86-
// HubSpot tracking (landing pages) — loader plus the
87-
// analytics/form-tracking/banner scripts it injects as <script> tags
88-
'https://*.hs-scripts.com',
89-
'https://*.hs-analytics.net',
90-
'https://*.hscollectedforms.net',
91-
'https://*.hs-banner.com',
9286
// X (Twitter) conversion pixel (landing pages) — the base code injects
9387
// uwt.js as a <script> tag from static.ads-twitter.com
9488
'https://static.ads-twitter.com',
@@ -134,10 +128,6 @@ const STATIC_CONNECT_SRC = [
134128
'https://*.g.doubleclick.net',
135129
// Google Ads conversion tag — conversion beacons
136130
'https://www.googleadservices.com',
137-
// HubSpot tracking — form-tracking API (hscollectedforms.js).
138-
// The visitor beacon itself is an image pixel (img-src, already
139-
// permitted below), not a connect-src request.
140-
'https://*.hscollectedforms.net',
141131
// X (Twitter) conversion pixel — uwt.js sends conversion beacons here
142132
// via fetch/sendBeacon. The t.co image-pixel fallback is already
143133
// covered by the `https:` wildcard in img-src.

‎packages/emcn/src/components/tab-strip/tab-strip.dom.test.tsx‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,74 @@ describe('TabStrip interactions', () => {
112112
expect(onSelect).toHaveBeenLastCalledWith('pinned', 'keyboard')
113113

114114
act(() => {
115+
tabButton('two').focus()
115116
tabButton('two').dispatchEvent(
116117
new KeyboardEvent('keydown', { key: 'Delete', bubbles: true, cancelable: true })
117118
)
118119
})
119120
expect(onClose).toHaveBeenCalledWith('two')
121+
expect(document.activeElement).toBe(tabButton('two'))
122+
})
123+
124+
it('restores focus to a committed survivor after an asynchronous multi-tab close', () => {
125+
const onClose = vi.fn()
126+
mount(renderStrip(tabs, vi.fn(), onClose))
127+
act(() => {
128+
tabButton('one').focus()
129+
tabButton('one').dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', bubbles: true }))
130+
})
120131
expect(document.activeElement).toBe(tabButton('one'))
132+
act(() => root?.render(renderStrip(tabs, vi.fn(), onClose)))
133+
expect(document.activeElement).toBe(tabButton('one'))
134+
135+
act(() => root?.render(renderStrip([{ ...tabs[0], active: true }], vi.fn(), onClose)))
136+
expect(document.activeElement).toBe(tabButton('pinned'))
137+
})
138+
139+
it('does not steal focus from another control when a pending close completes', () => {
140+
mount(renderStrip(tabs))
141+
act(() => {
142+
tabButton('one').focus()
143+
tabButton('one').dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', bubbles: true }))
144+
})
145+
const add = container?.querySelector<HTMLButtonElement>('[aria-label="New tab"]')
146+
act(() => add?.focus())
147+
act(() => root?.render(renderStrip([{ ...tabs[0], active: true }])))
148+
expect(document.activeElement).toBe(add)
149+
})
150+
151+
it('exposes a shared DOM ancestor for multi-tab drag images', () => {
152+
const onTabDragStart = vi.fn((event) => {
153+
const strip = event.currentTarget.closest('[data-tab-strip]')
154+
expect(strip?.querySelectorAll('[data-tab-strip-item]')).toHaveLength(3)
155+
})
156+
mount(<TabStrip tabs={tabs} onSelect={vi.fn()} onTabDragStart={onTabDragStart} />)
157+
act(() => stripItem('two').dispatchEvent(dragStartEvent()))
158+
expect(onTabDragStart).toHaveBeenCalledOnce()
159+
})
160+
161+
it('follows the active survivor across staggered tab removals', () => {
162+
mount(renderStrip(tabs))
163+
act(() => tabButton('one').focus())
164+
act(() => root?.render(renderStrip([tabs[0], { ...tabs[2], active: true }])))
165+
expect(document.activeElement).toBe(tabButton('two'))
166+
act(() => root?.render(renderStrip([{ ...tabs[0], active: true }])))
167+
expect(document.activeElement).toBe(tabButton('pinned'))
168+
})
169+
170+
it('relinquishes focus ownership when the user leaves the strip', () => {
171+
mount(renderStrip(tabs))
172+
const outside = document.createElement('button')
173+
document.body.append(outside)
174+
try {
175+
act(() => tabButton('one').focus())
176+
act(() => outside.focus())
177+
act(() => outside.blur())
178+
act(() => root?.render(renderStrip([{ ...tabs[0], active: true }])))
179+
expect(document.activeElement).toBe(document.body)
180+
} finally {
181+
outside.remove()
182+
}
121183
})
122184

123185
it('identifies pointer selection separately from keyboard navigation', () => {

0 commit comments

Comments
 (0)