Skip to content
Open
Show file tree
Hide file tree
Changes from 131 commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
4e2b539
feat(widget-configurator): enhance configurator with new controls and…
fairlighteth Apr 6, 2026
4f10af7
feat: add Widget App URL param to configurator
Danziger Apr 7, 2026
b9ef0f3
feat: various UI improvements on the widget configurator, plus additi…
Danziger Apr 8, 2026
94a6a6b
fix: fix sidebar resizing
Danziger Apr 8, 2026
99e0e46
feat: add chess pattern behind widget
Danziger Apr 8, 2026
e2b3528
feat: update AccordionSection style to be as wide as possible to maxi…
Danziger Apr 8, 2026
a03bbd9
feat: allow only one expanded accordion at a time
Danziger Apr 8, 2026
88a0d52
feat: break down configurator into smaller components
Danziger Apr 8, 2026
e037f0d
feat: add autoResizeEnabled prop to avoid scrollbar flash while the i…
Danziger Apr 9, 2026
b36c65c
feat: improve sidebar and snippet layout and styles
Danziger Apr 9, 2026
c871585
refactor: restructure directories
Danziger Apr 9, 2026
d706403
refactor: restructure directories
Danziger Apr 9, 2026
34a202b
refactor: better organize widget-configurator app directories and files
Danziger Apr 9, 2026
99d6046
chore: sort out configurator params in various files for improved red…
Danziger Apr 9, 2026
0e761e2
fix: pass baseUrl to widget
Danziger Apr 9, 2026
edcf620
feat: add some custom input components to simplify/dry of the overall…
Danziger Apr 9, 2026
0701b33
feat: add some custom input components to simplify/dry of the overall…
Danziger Apr 9, 2026
0891009
feat: add some layout presets (WIP)
Danziger Apr 10, 2026
f0a8279
fix: merge develop, resolve conflics and fix memory leak related to w…
Danziger Apr 13, 2026
8ca2623
Merge branch 'develop' into feat/widget-prototype
Danziger Apr 13, 2026
61e8410
Merge branch 'develop' into feat/widget-prototype
Danziger Apr 13, 2026
b08c430
feat: add toggle for disablePostTradeTips
Danziger Apr 13, 2026
a6f31b2
feat: add toggle for disablePostTradeTips
Danziger Apr 13, 2026
196e479
chore: sort out COMMENTS_BY_PARAM_NAME
Danziger Apr 13, 2026
19f2bc5
feat: add field for appCode
Danziger Apr 13, 2026
6768663
feat: add env badge/indicator in widget sidebar header
Danziger Apr 13, 2026
f6ac9d3
feat: allow wider sidebar to use it as mobile mode
Danziger Apr 13, 2026
a8bcc4f
feat: add loader and update widget using React transitions
Danziger Apr 14, 2026
5f69acf
chore: merge develop and resolve conflicts
Danziger Apr 30, 2026
545ee44
fix: fix tests
Danziger Apr 30, 2026
b049dc9
fix: fix tests
Danziger Apr 30, 2026
a2bfcdf
fix: fix widget build
Danziger Apr 30, 2026
ffbee91
feat: make widget configurator theme selector work
Danziger Apr 30, 2026
5c15546
fix: remove missleading Auto option from theme selector
Danziger Apr 30, 2026
cfa9326
feat: add PresetsButtons and add presets for layout and baseUrl
Danziger May 1, 2026
e715300
Merge branch 'develop' into feat/widget-prototype
Danziger May 8, 2026
fe64dd6
refactor: break down widget configurator into smaller form panels and…
Danziger May 8, 2026
5c807b4
fix: improve generics for AccordionFormSection
Danziger May 8, 2026
30564ba
chore: merge develop and resolve conflicts
Danziger May 20, 2026
97288e8
chore: merge develop and resolve conflicts
Danziger May 21, 2026
78e0996
feat: refactor most inputs to use reusable base components and addres…
Danziger May 21, 2026
8eb3df1
chore: merge develop and resolve conflicts
Danziger May 22, 2026
7342617
Merge branch 'develop' into feat/widget-prototype
Danziger May 25, 2026
e7b0be1
chore: merge develop and resolve conflicts
Danziger May 25, 2026
50d4549
Merge branch 'develop' into feat/widget-prototype
Danziger May 25, 2026
f2a4e15
chore: merge develop and resolve conflicts
Danziger May 27, 2026
2b46b76
chore: merge develop and resolve conflicts
Danziger Jun 1, 2026
a1c2989
fix: simplify widget configurator input change handling
Danziger Jun 2, 2026
8a149a5
fix: remove id attr from widget configurator inputs
Danziger Jun 2, 2026
f382417
feat: finish simplifying configurator inputs
Danziger Jun 2, 2026
a3c9fcf
fix: fix sidebar drag handler being on top of modals
Danziger Jun 2, 2026
c1ca869
fix: fix input label alignment and add custom tokens modal styles
Danziger Jun 2, 2026
9b9743a
fix: consistent styleing for buttons and modals/dropdowns/surfaces
Danziger Jun 2, 2026
0cb12d3
feat: improve tabs
Danziger Jun 2, 2026
3ec3077
fix: fix TS issue with button styles
Danziger Jun 2, 2026
b46ec25
fix: fix global (html) background
Danziger Jun 2, 2026
9ed0a83
feat: update AddCustomListDialob backdrop
Danziger Jun 2, 2026
a29443b
feat: improve Select styling
Danziger Jun 2, 2026
3572365
feat: further simplify Select / MultiSelect inputs
Danziger Jun 2, 2026
0620c5c
fix: get rid of TokenListControl and just inline inputs in TokensSect…
Danziger Jun 2, 2026
98d86ee
fix: move/rename AddCustomTokensDialog
Danziger Jun 2, 2026
06f97c1
feat: update ColorInput styles to match the rest of the inputs and to…
Danziger Jun 3, 2026
01f0c3e
fix: update inputs focused border thickness and color
Danziger Jun 3, 2026
5a034f5
fix: fix Select label and border overlap
Danziger Jun 3, 2026
1167e13
fix: change default layout
Danziger Jun 3, 2026
43a1681
feat: add unit to NumberInput
Danziger Jun 3, 2026
957f5a4
Merge branch 'develop' into feat/widget-prototype
Danziger Jun 3, 2026
29cb651
fix: remove TODOs in sidebar.component.tsx
Danziger Jun 3, 2026
c9b59fb
fix: minor UI spacing fixes
Danziger Jun 3, 2026
6c2a59d
feat: persist configurator values
Danziger Jun 3, 2026
555d894
chore: move directory
Danziger Jun 3, 2026
f2fdbdd
chore: move directory
Danziger Jun 3, 2026
200bd78
feat: add RadioGroupInput
Danziger Jun 3, 2026
775e4a3
feat: fix/improve code highligher styles and visibility
Danziger Jun 3, 2026
3ec3e2b
feat: add a couple new helper texts
Danziger Jun 3, 2026
e732d27
feat: rename autoResizeIframe to disableScrollbars and add more detai…
Danziger Jun 3, 2026
4a3741b
feat: remove appWrapper custom styles as it overlaps with bodyWrapper…
Danziger Jun 3, 2026
dbecc9d
chore: merge develop and resolve conflicts
Danziger Jun 4, 2026
b17c2a9
fix: update page title
Danziger Jun 4, 2026
37e45b7
feat: add an option to select older SDK versions
Danziger Jun 4, 2026
7522c01
feat: add helper text to token lists
Danziger Jun 4, 2026
f18204e
fix: remove custom tooltip and update MUI tooltip and toast styles
Danziger Jun 4, 2026
c8be91e
feat: use react-feather across widget-configurator
Danziger Jun 4, 2026
1d40c50
feat: allow entering any token again
Danziger Jun 4, 2026
02ca862
Merge branch 'develop' into feat/widget-prototype
Danziger Jun 4, 2026
468a795
fix: fix failing tests
Danziger Jun 4, 2026
1e97b68
Merge branch 'feat/widget-prototype' of github.com:cowprotocol/cowswa…
Danziger Jun 4, 2026
4487fec
fix: fix palette reset issue
Danziger Jun 4, 2026
1863a74
fix: fix assignElementStyles
Danziger Jun 4, 2026
ac04640
fix: fix Vercel build by declaring bs58 as a dependency of @reown/app…
Danziger Jun 4, 2026
f2ca87e
fix: replace useThrottledFn with an package that fixes propagation is…
Danziger Jun 4, 2026
bf6b11d
fix: remove widgetPadding and widgetBorderRadius options
Danziger Jun 4, 2026
07983e9
chore: simplify CustomizationSectionForm input change handler
Danziger Jun 4, 2026
1026d2f
fix: address some ESLint ignores in AddCustomTokensDialog
Danziger Jun 4, 2026
e5bbeb4
feat: add TSDoc comment about sanitization in vanillaNoDepsExample
Danziger Jun 4, 2026
00df49d
fix: require effectiveChainId in buildConfiguratorState
Danziger Jun 4, 2026
ce89c70
fix: restore widget-configurator AGENTS.md
Danziger Jun 4, 2026
11a83d2
fix: re-enable typechecks
Danziger Jun 4, 2026
106770d
Merge branch 'develop' into feat/widget-prototype
Danziger Jun 9, 2026
cb0a303
fix: rename .spec to .test
Danziger Jun 9, 2026
051e5f7
fix: remove unused dependency
Danziger Jun 9, 2026
f1e70e9
chore: change iframe ID and extract to constant
Danziger Jun 9, 2026
5ffde50
fix: remove value added to common-utils/tsconfig.lib.json by accident
Danziger Jun 9, 2026
05061a6
fix: remove deepMerge
Danziger Jun 9, 2026
fc3a941
Merge branch 'develop' into feat/widget-prototype
fairlighteth Jun 9, 2026
1536906
feat: update widget SDK selector labels
Danziger Jun 9, 2026
87839fa
fix: adjust dark text colors to match Explorer
Danziger Jun 9, 2026
d4ef9f3
fix: make preview panel work just like before for older SDK versions
Danziger Jun 9, 2026
35eba61
fix: make showIframeOutline default value = false
Danziger Jun 9, 2026
79282ba
fix: escape HTML snippet params
Danziger Jun 9, 2026
e5b0cd7
fix: add supported chain check in useSyncWidgetNetwork
Danziger Jun 10, 2026
e0fa467
fix: update sanitizeParamters logic
Danziger Jun 10, 2026
8768a24
fix: fix disableScrollbars missing cleanup
Danziger Jun 10, 2026
62d6ca4
fix: fix palette URL param parsing
Danziger Jun 10, 2026
e20c4d5
fix: updated param handling to allow deprecated ones still
Danziger Jun 10, 2026
ca50db8
fix: simplify shouldPropagateHeightUpdates
Danziger Jun 10, 2026
9562b61
fix: address remaining nitpicks
Danziger Jun 10, 2026
994d5de
fix: remove isUpToSmall from UPDATE_HEIGHT
Danziger Jun 10, 2026
2181437
fix: update mapWidgetTheme to be backwards compatible with older params
Danziger Jun 10, 2026
a7ac6e9
chore: merge develop and resolve conflicts
Danziger Jun 10, 2026
f9b36f4
fix: update cow-fi widget integration to new params
Danziger Jun 10, 2026
f23bee0
fix: remove unused palette color
Danziger Jun 10, 2026
676d30f
fix: remove duplicate StrictMode and remove harcoded widget-lib versi…
Danziger Jun 10, 2026
19f804f
chore: move csstype to devDependencies
Danziger Jun 10, 2026
b297a40
fix: use merged widget params on iframe create and guard snippet trad…
Danziger Jun 10, 2026
7ab0533
fix: revert change to widget mode paddings
Danziger Jun 10, 2026
2c7f78a
fix: fix TS error
Danziger Jun 10, 2026
1984012
fix: add missing initial state to useResizableDrawerWidth
Danziger Jun 10, 2026
555c80d
fix: make Local SDK version available in preview envs
Danziger Jun 10, 2026
24eed97
fix: fix logic to parse deadline fields
Danziger Jun 10, 2026
07dde28
chore: merge develop and resolve conflicts
Danziger Jun 15, 2026
3341a37
Merge branch 'develop' into feat/widget-prototype
Danziger Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/cow-fi/app/(main)/widget/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const widgetParams: CowSwapWidgetParams = {
appCode: 'CoW Protocol: Widget Demo',
theme: 'light',
standaloneMode: true,
width: '100%',
iframeStyle: { width: '100%' },
}

export default function Page(): ReactNode {
Expand Down
18 changes: 0 additions & 18 deletions apps/cowswap-frontend/src/common/hooks/useThrottleFn.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { render } from '@testing-library/react'
import { ThemeProvider as StyledComponentsThemeProvider } from 'styled-components/macro'

import { AppWrapper, BodyWrapper, Marginer } from './styled'

describe('AppWrapper', () => {
it('does not enforce a minimum viewport height in widget mode', () => {
const { getByTestId } = render(
<StyledComponentsThemeProvider theme={{ isWidget: true } as never}>
<AppWrapper data-testid="app-wrapper" />
</StyledComponentsThemeProvider>,
)

expect(window.getComputedStyle(getByTestId('app-wrapper')).minHeight).toBe('auto')
})

it('keeps the full-page minimum height outside widget mode', () => {
const { getByTestId } = render(
<StyledComponentsThemeProvider theme={{ isWidget: false } as never}>
<AppWrapper data-testid="app-wrapper" />
</StyledComponentsThemeProvider>,
)

expect(window.getComputedStyle(getByTestId('app-wrapper')).minHeight).toBe('100vh')
})
})

describe('BodyWrapper', () => {
it('does not flex-grow in widget mode', () => {
const { getByTestId } = render(
<StyledComponentsThemeProvider theme={{ isWidget: true } as never}>
<BodyWrapper data-testid="body-wrapper" />
</StyledComponentsThemeProvider>,
)

expect(window.getComputedStyle(getByTestId('body-wrapper')).flex).toBe('0 0 auto')
})

it('uses the legacy widget shell padding by default', () => {
const { getByTestId } = render(
<StyledComponentsThemeProvider theme={{ isWidget: true } as never}>
<BodyWrapper data-testid="body-wrapper" />
</StyledComponentsThemeProvider>,
)

const computedStyle = window.getComputedStyle(getByTestId('body-wrapper'))

expect(computedStyle.paddingTop).toBe('16px')
expect(computedStyle.paddingRight).toBe('16px')
expect(computedStyle.paddingBottom).toBe('0px')
expect(computedStyle.paddingLeft).toBe('16px')
})

it('keeps the legacy flex behavior outside widget mode', () => {
const { getByTestId } = render(
<StyledComponentsThemeProvider theme={{ isWidget: false } as never}>
<BodyWrapper data-testid="body-wrapper" />
</StyledComponentsThemeProvider>,
)

expect(window.getComputedStyle(getByTestId('body-wrapper')).flex).toBe('1 1 auto')
})
})

describe('Marginer', () => {
it('does not add vertical spacing in widget mode', () => {
const { getByTestId } = render(
<StyledComponentsThemeProvider theme={{ isWidget: true } as never}>
<Marginer data-testid="marginer" />
</StyledComponentsThemeProvider>,
)

expect(window.getComputedStyle(getByTestId('marginer')).marginTop).toBe('0px')
})

it('keeps the legacy spacing outside widget mode', () => {
const { getByTestId } = render(
<StyledComponentsThemeProvider theme={{ isWidget: false } as never}>
<Marginer data-testid="marginer" />
</StyledComponentsThemeProvider>,
)

expect(window.getComputedStyle(getByTestId('marginer')).marginTop).toBe('5rem')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ export const AppWrapper = styled.div<Partial<CSS.Properties>>`
display: flex;
flex-flow: column;
align-items: flex-start;
min-height: ${({ theme }) => (theme.isWidget ? '400px' : '100vh')};
min-height: ${({ theme }) => (theme.isWidget ? 'auto' : '100vh')};
height: ${({ theme }) => (theme.isWidget ? 'initial' : '100%')};
position: relative;
`

export const Marginer = styled.div`
margin-top: 5rem;
margin-top: ${({ theme }) => (theme.isWidget ? '0' : '5rem')};
`

export const SceneContainer = styled.div`
Expand Down Expand Up @@ -72,7 +72,7 @@ export const BodyWrapper = styled.div<{
width: 100%;
align-items: flex-start;
justify-content: center;
flex: 1 1 auto;
flex: ${({ theme }) => (theme.isWidget ? '0 0 auto' : '1 1 auto')};
z-index: 2;
color: inherit;
padding: ${({ theme, $hasActiveSpeechBubbleNotification }) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ReactNode, useMemo, useState } from 'react'
import { type CSSProperties, type ReactNode, useMemo, useState } from 'react'

import { useAnalyticsReporter } from '@cowprotocol/analytics'
import { useFeatureFlags, useMediaQuery } from '@cowprotocol/common-hooks'
Expand All @@ -7,6 +7,8 @@ import type { NotificationModel } from '@cowprotocol/core'
import { Footer, Media } from '@cowprotocol/ui'
import { useWalletDetails, useWalletInfo } from '@cowprotocol/wallet'

import { useInjectedWidgetParams } from 'entities/injectedWidget'

import { URLWarning } from 'legacy/components/Header/URLWarning'
import { useDarkModeManager } from 'legacy/state/user/hooks'

Expand Down Expand Up @@ -70,6 +72,7 @@ export function AppContainer({ children }: AppContainerProps): ReactNode {

useInitializeUtm()
const isInjectedWidgetMode = isInjectedWidget()
const { bodyWrapperStyle } = useInjectedWidgetParams()
const [darkMode] = useDarkModeManager()
const [pageBackgroundVariant, setPageBackgroundVariant] = useState<PageBackgroundVariant>('default')
const [pageScene, setPageScene] = useState<ReactNode | null>(null)
Expand Down Expand Up @@ -114,6 +117,8 @@ export function AppContainer({ children }: AppContainerProps): ReactNode {
{isYieldEnabled && <CoWAmmBanner />}

<styledEl.BodyWrapper
id="bodyWrapper"
style={isInjectedWidgetMode ? (bodyWrapperStyle as CSSProperties) : undefined}
customTheme={customTheme}
backgroundVariant={pageBackgroundVariant}
$hasActiveSpeechBubbleNotification={hasActiveSpeechBubbleNotification}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { isInjectedWidget } from '@cowprotocol/common-utils'

import { render, screen } from '@testing-library/react'

import { BalancesDevtools } from './BalancesDevtools'

jest.mock('@cowprotocol/common-utils', () => ({
isInjectedWidget: jest.fn(),
}))

jest.mock('jotai-devtools', () => ({
DevTools: () => <div data-testid="balances-devtools" />,
}))

const isInjectedWidgetMock = isInjectedWidget as jest.MockedFunction<typeof isInjectedWidget>

describe('BalancesDevtools', () => {
const originalNodeEnv = process.env.NODE_ENV

beforeEach(() => {
isInjectedWidgetMock.mockReturnValue(false)
})

afterEach(() => {
process.env.NODE_ENV = originalNodeEnv
})

it('renders in development outside widget mode', () => {
process.env.NODE_ENV = 'development'

render(<BalancesDevtools />)

expect(screen.getByTestId('balances-devtools')).toBeTruthy()
})

it('does not render inside widget mode', () => {
process.env.NODE_ENV = 'development'
isInjectedWidgetMock.mockReturnValue(true)

render(<BalancesDevtools />)

expect(screen.queryByTestId('balances-devtools')).toBeNull()
})
})
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { JSX } from 'react'

import { isInjectedWidget } from '@cowprotocol/common-utils'

import { DevTools } from 'jotai-devtools'

export function BalancesDevtools(): JSX.Element | null {
if (process.env.NODE_ENV !== 'development') return null
if (process.env.NODE_ENV !== 'development' || isInjectedWidget()) return null

return <DevTools position="bottom-left" />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createElement, type ReactNode } from 'react'

import { renderHook, type RenderHookResult } from '@testing-library/react'
import { MemoryRouter } from 'react-router'

import { useInjectedWidgetPalette } from './useInjectedWidgetPalette'

function renderPaletteHook(search: string): RenderHookResult<ReturnType<typeof useInjectedWidgetPalette>, unknown> {
return renderHook(() => useInjectedWidgetPalette(), {
wrapper: ({ children }: { children: ReactNode }) =>
createElement(MemoryRouter, { initialEntries: [`/widget${search}`] }, children),
})
}

describe('useInjectedWidgetPalette', () => {
it('returns undefined when the palette param is absent', () => {
const { result } = renderPaletteHook('')

expect(result.current).toBeUndefined()
})

it('returns null when palette=null', () => {
const { result } = renderPaletteHook('?palette=null')

expect(result.current).toBeNull()
})

it('returns parsed palette JSON from the URL', () => {
const palette = { paper: '#ff0', primary: '#052b65' }
const { result } = renderPaletteHook(`?palette=${encodeURIComponent(JSON.stringify(palette))}`)

expect(result.current).toEqual(palette)
})

it('returns null when the palette param cannot be parsed', () => {
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => undefined)

const { result } = renderPaletteHook('?palette=not-valid-json')

expect(result.current).toBeNull()
expect(consoleError).toHaveBeenCalled()

consoleError.mockRestore()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,36 @@ import { CowSwapWidgetPalette } from '@cowprotocol/widget-lib'

import { useLocation } from 'react-router'

// The theme palette provided by a consumer
export function useInjectedWidgetPalette(): Partial<CowSwapWidgetPalette> | undefined {
/**
* Palette from the widget URL query string.
*
* - No param (`palette === null`): Keep the last applied custom palette.
* - Explicitly set to null (`palette === "null"`): Reset to the default theme.
* - Unparseable `palette` value: Reset to the default theme.
* - object: custom palette JSON from the host.
*/
export function useInjectedWidgetPalette(): Partial<CowSwapWidgetPalette> | null | undefined {
const { search } = useLocation()

return useMemo(() => {
const searchParams = new URLSearchParams(search)
const palette = searchParams.get('palette')

// When the palette is not provided, then do nothing
if (!palette) return undefined
// When the palette param is absent, do not change the current palette state.
if (palette === null) {
return undefined
}

// Reset palette state when the value is null
// Explicit reset to defaults (see widget-lib `addThemePaletteToQuery`).
if (palette === 'null') {
return undefined
return null
}

try {
return JSON.parse(decodeURIComponent(palette))
return JSON.parse(decodeURIComponent(palette)) as Partial<CowSwapWidgetPalette>
} catch (e) {
console.error('Failed to parse palette from URL', e)
return null
}

return undefined
}, [search])
}
Loading
Loading