Skip to content

Commit ed288c2

Browse files
committed
fix: use unique ids for gradients
1 parent 6e3da8f commit ed288c2

File tree

11 files changed

+70
-33
lines changed

11 files changed

+70
-33
lines changed

.changeset/tall-masks-appear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@lglab/react-qr-code': patch
3+
---
4+
5+
Fix issue with multiple QR codes sharing same gradient

apps/playground/src/App.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ function App() {
123123
type: 'linear',
124124
rotation: 45,
125125
stops: [
126-
{ offset: '0%', color: '#ff7e5f' },
127-
{ offset: '100%', color: '#feb47b' },
126+
{ offset: '0%', color: '#00f' },
127+
{ offset: '100%', color: '#f00' },
128128
],
129129
}}
130130
size={256}
@@ -141,6 +141,18 @@ function App() {
141141
size={256}
142142
value='https://reactqrcode.com'
143143
/>
144+
<ReactQRCode
145+
gradient={{
146+
type: 'linear',
147+
rotation: 45,
148+
stops: [
149+
{ offset: '0%', color: '#f00' },
150+
{ offset: '100%', color: '#0f0' },
151+
],
152+
}}
153+
size={256}
154+
value='https://reactqrcode.com'
155+
/>
144156
</div>
145157
</div>
146158
)

packages/react-qr-code/src/components/background.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
import { useId } from 'react'
2-
3-
import { BG_GRADIENT_ID } from '../constants'
41
import type { BackgroundSettings } from '../types/lib'
52
import { calculateGradientVectors } from '../utils/svg'
63

74
interface BackgroundProps {
85
background?: BackgroundSettings
6+
bgGradientId: string
97
numCells: number
108
}
119

1210
const testProps = {
1311
'data-testid': 'background',
1412
}
1513

16-
export const Background = ({ background, numCells }: BackgroundProps) => {
17-
const uuid = useId()
18-
const backgroundId = `${BG_GRADIENT_ID}-${uuid}`
14+
export const Background = ({ background, bgGradientId, numCells }: BackgroundProps) => {
1915
if (!background) {
2016
return null
2117
}
@@ -32,14 +28,14 @@ export const Background = ({ background, numCells }: BackgroundProps) => {
3228
<>
3329
<defs>
3430
{background.type === 'linear' ? (
35-
<linearGradient id={backgroundId} gradientUnits='userSpaceOnUse' {...vectors}>
31+
<linearGradient id={bgGradientId} gradientUnits='userSpaceOnUse' {...vectors}>
3632
{background.stops?.map((stop, index) => (
3733
<stop key={index} offset={stop.offset} stopColor={stop.color} />
3834
))}
3935
</linearGradient>
4036
) : (
4137
<radialGradient
42-
id={backgroundId}
38+
id={bgGradientId}
4339
gradientUnits='userSpaceOnUse'
4440
cx='50%'
4541
cy='50%'
@@ -52,7 +48,7 @@ export const Background = ({ background, numCells }: BackgroundProps) => {
5248
)}
5349
</defs>
5450
<path
55-
fill={`url(#${backgroundId})`}
51+
fill={`url(#${bgGradientId})`}
5652
d={`M0,0 h${numCells}v${numCells}H0z`}
5753
{...testProps}
5854
/>

packages/react-qr-code/src/components/data-modules.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type ReactNode, useCallback, useMemo } from 'react'
22

3-
import { DEFAULT_NUM_STAR_POINTS, GRADIENT_ID } from '../constants'
3+
import { DEFAULT_NUM_STAR_POINTS } from '../constants'
44
import type { DataModulesProps } from '../types/utils'
55
import {
66
bottomRounded,
@@ -31,6 +31,7 @@ export const DataModules = ({
3131
margin,
3232
settings,
3333
gradient,
34+
gradientId,
3435
}: DataModulesProps): ReactNode => {
3536
const { color, style, randomSize } = useMemo(
3637
() => sanitizeDataModulesSettings(settings),
@@ -150,7 +151,7 @@ export const DataModules = ({
150151
})
151152
return (
152153
<path
153-
fill={gradient ? `url(#${GRADIENT_ID})` : color}
154+
fill={gradient ? `url(#${gradientId})` : color}
154155
d={ops.join('')}
155156
shapeRendering={style === 'square' ? 'crispEdges' : 'geometricPrecision'}
156157
data-testid='data-modules'

packages/react-qr-code/src/components/finder-patterns-inner.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
FINDER_PATTERN_INNER_SIZE,
77
FINDER_PATTERN_OUTER_ROTATIONS,
88
FINDER_PATTERN_SIZE,
9-
GRADIENT_ID,
109
} from '../constants'
1110
import type { FinderPatternsInnerProps } from '../types/utils'
1211
import {
@@ -25,12 +24,13 @@ export const FinderPatternsInner = ({
2524
margin,
2625
settings,
2726
gradient,
27+
gradientId,
2828
}: FinderPatternsInnerProps): ReactNode => {
2929
const { color, style } = useMemo(
3030
() => sanitizeFinderPatternInnerSettings(settings),
3131
[settings],
3232
)
33-
const fill = gradient ? `url(#${GRADIENT_ID})` : color
33+
const fill = gradient ? `url(#${gradientId})` : color
3434

3535
const coordinates = useMemo(
3636
() => [

packages/react-qr-code/src/components/finder-patterns-outer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
FINDER_PATTERN_OUTER_RADIUSES,
55
FINDER_PATTERN_OUTER_ROTATIONS,
66
FINDER_PATTERN_SIZE,
7-
GRADIENT_ID,
87
} from '../constants'
98
import type { FinderPatternsOuterProps } from '../types/utils'
109
import {
@@ -23,12 +22,13 @@ export const FinderPatternsOuter = ({
2322
margin,
2423
settings,
2524
gradient,
25+
gradientId,
2626
}: FinderPatternsOuterProps): ReactNode => {
2727
const { style, color } = useMemo(
2828
() => sanitizeFinderPatternOuterSettings(settings),
2929
[settings],
3030
)
31-
const fill = gradient ? `url(#${GRADIENT_ID})` : color
31+
const fill = gradient ? `url(#${gradientId})` : color
3232

3333
const ops: Array<string> = []
3434

packages/react-qr-code/src/components/gradient.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { GRADIENT_ID } from '../constants'
21
import type { GradientSettings } from '../types/lib'
32
import { calculateGradientVectors } from '../utils/svg'
43

54
interface GradientProps {
65
gradient?: GradientSettings
6+
gradientId: string
77
}
88

9-
export const Gradient = ({ gradient }: GradientProps) => {
9+
export const Gradient = ({ gradient, gradientId }: GradientProps) => {
1010
if (!gradient) {
1111
return null
1212
}
@@ -16,14 +16,14 @@ export const Gradient = ({ gradient }: GradientProps) => {
1616
return (
1717
<defs>
1818
{gradient.type === 'linear' ? (
19-
<linearGradient id={GRADIENT_ID} gradientUnits='userSpaceOnUse' {...vectors}>
19+
<linearGradient id={gradientId} gradientUnits='userSpaceOnUse' {...vectors}>
2020
{gradient.stops?.map((stop, index) => (
2121
<stop key={index} offset={stop.offset} stopColor={stop.color} />
2222
))}
2323
</linearGradient>
2424
) : (
2525
<radialGradient
26-
id={GRADIENT_ID}
26+
id={gradientId}
2727
gradientUnits='userSpaceOnUse'
2828
cx='50%'
2929
cy='50%'
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useId } from 'react'
2+
3+
import { BG_GRADIENT_ID, GRADIENT_ID } from '../constants'
4+
5+
export const useIds = () => {
6+
const uuid = useId()
7+
8+
return {
9+
gradientId: `${GRADIENT_ID}-${uuid}`,
10+
bgGradientId: `${BG_GRADIENT_ID}-${uuid}`,
11+
}
12+
}

packages/react-qr-code/src/react-qr-code.test.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,26 +184,30 @@ describe('ReactQRCode', () => {
184184
}
185185
const { container } = render(<ReactQRCode value='test' gradient={gradient} />)
186186

187-
const gradientElement = container.querySelector(`${selector}#${GRADIENT_ID}`)
188-
const stops = container.querySelectorAll(`${selector}#${GRADIENT_ID} stop`)
187+
const gradientElement = container.querySelector(
188+
`${selector}[id^="${GRADIENT_ID}-"]`,
189+
)
190+
const stops = gradientElement?.querySelectorAll('stop')
189191

190192
expect(gradientElement).toBeInTheDocument()
191193
expect(screen.getByTestId('data-modules')).toHaveAttribute(
192194
'fill',
193-
`url(#${GRADIENT_ID})`,
195+
`url(#${gradientElement?.id})`,
194196
)
195197
screen.getAllByTestId('finder-patterns-outer').forEach((path) => {
196-
expect(path).toHaveAttribute('fill', `url(#${GRADIENT_ID})`)
198+
expect(path).toHaveAttribute('fill', `url(#${gradientElement?.id})`)
197199
})
198200
screen.getAllByTestId('finder-patterns-inner').forEach((path) => {
199-
expect(path).toHaveAttribute('fill', `url(#${GRADIENT_ID})`)
201+
expect(path).toHaveAttribute('fill', `url(#${gradientElement?.id})`)
200202
})
201203

202204
expect(stops).toHaveLength(2)
203-
expect(stops[0]).toHaveAttribute('stop-color', gradient.stops[0].color)
204-
expect(stops[0]).toHaveAttribute('offset', gradient.stops[0].offset)
205-
expect(stops[1]).toHaveAttribute('stop-color', gradient.stops[1].color)
206-
expect(stops[1]).toHaveAttribute('offset', gradient.stops[1].offset)
205+
if (stops) {
206+
expect(stops[0]).toHaveAttribute('stop-color', gradient.stops[0].color)
207+
expect(stops[0]).toHaveAttribute('offset', gradient.stops[0].offset)
208+
expect(stops[1]).toHaveAttribute('stop-color', gradient.stops[1].color)
209+
expect(stops[1]).toHaveAttribute('offset', gradient.stops[1].offset)
210+
}
207211
})
208212
})
209213

packages/react-qr-code/src/react-qr-code.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
DEFAULT_MINVERSION,
1212
DEFAULT_SIZE,
1313
} from './constants'
14+
import { useIds } from './hooks/use-ids'
1415
import { useQRCode } from './hooks/use-qr-code'
1516
import type { DownloadOptions, ReactQRCodeProps } from './types/lib'
1617
import { downloadRaster, downloadSVG } from './utils/download'
@@ -35,7 +36,7 @@ const ReactQRCode = (props: ReactQRCodeProps) => {
3536
} = props
3637

3738
const svgRef = useRef<SVGSVGElement | null>(null)
38-
39+
const { gradientId, bgGradientId } = useIds()
3940
const { margin, cells, numCells, calculatedImageSettings } = useQRCode({
4041
value,
4142
level,
@@ -99,6 +100,7 @@ const ReactQRCode = (props: ReactQRCodeProps) => {
99100
modules,
100101
margin,
101102
gradient,
103+
gradientId,
102104
}
103105

104106
return (
@@ -111,8 +113,12 @@ const ReactQRCode = (props: ReactQRCodeProps) => {
111113
aria-label={svgProps?.['aria-label'] || 'QR Code'}
112114
{...svgProps}
113115
>
114-
<Gradient gradient={gradient} />
115-
<Background background={background} numCells={numCells} />
116+
<Gradient gradient={gradient} gradientId={gradientId} />
117+
<Background
118+
background={background}
119+
bgGradientId={bgGradientId}
120+
numCells={numCells}
121+
/>
116122
<FinderPatternsOuter settings={finderPatternOuterSettings} {...svgElementsProps} />
117123
<FinderPatternsInner settings={finderPatternInnerSettings} {...svgElementsProps} />
118124
<DataModules settings={dataModulesSettings} {...svgElementsProps} />

0 commit comments

Comments
 (0)