From de67ddf79f13124eb8cda85a4b563e92c7058697 Mon Sep 17 00:00:00 2001 From: Rob Cameron Date: Thu, 23 May 2024 08:54:03 -0700 Subject: [PATCH] In useOgImage() hook adds searchParams option for setting arbitrary query string vars to generated URL (#10677) Found a missing feature while working on adding og:image support to Cambium: Right now setting the width or height of an og:image will add those properties as query string variables in the URL that's generated: http://localhost:8910/photo.png?width=1000&height=800 But you may need variables that you'll use to modify the image you generate. We need a way to also add these to the final URL: http://localhost:8910/photo.png?brightness=0.8&contrast=1.5 I'm thinking of having useOgImage() accept an additional key in its object argument, which is an object containing all of the vars you want to append to the URL. This can be a simple object or an instance of URLSearchParams and we'll do the right thing: const { url } = useOgImage({ searchParams: { brightness: 0.9, contrast: 1.5 } }) // url => http://localhost:8910/photo.png?brightness=0.9&contrast=1.5 --- .changesets/10677.md | 6 ++++ packages/ogimage-gen/src/hooks.test.ts | 45 +++++++++++++++++++++++++- packages/ogimage-gen/src/hooks.ts | 22 +++++++++---- 3 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 .changesets/10677.md diff --git a/.changesets/10677.md b/.changesets/10677.md new file mode 100644 index 000000000000..c97de900eb7a --- /dev/null +++ b/.changesets/10677.md @@ -0,0 +1,6 @@ +- Adds `searchParams` option to `useOgImage()` hook for adding arbitrary query string vars to generated URL (#10677) by @cannikin + +``` +const { url } = useOgImage({ searchParams: { foo: 'bar' }) +console.log(url) // => http://localhost:8910/photo.png?foo=bar +``` diff --git a/packages/ogimage-gen/src/hooks.test.ts b/packages/ogimage-gen/src/hooks.test.ts index 29f425a9f76c..ca43594b693d 100644 --- a/packages/ogimage-gen/src/hooks.test.ts +++ b/packages/ogimage-gen/src/hooks.test.ts @@ -101,6 +101,48 @@ describe('useOgImage', () => { expect(url).toBe('http://localhost/about.png?foo=bar') }) + test('can include additional query variables in the form of URLSearchParams', () => { + mockLocation.mockReturnValue({ + origin: 'http://localhost', + pathname: '/about', + searchParams: new URLSearchParams('foo=bar'), + }) + + const { url } = useOgImage({ + searchParams: new URLSearchParams({ baz: 'qux' }), + }) + + expect(url).toBe('http://localhost/about.png?foo=bar&baz=qux') + }) + + test('can include additional query variables in the form of an object', () => { + mockLocation.mockReturnValue({ + origin: 'http://localhost', + pathname: '/about', + searchParams: new URLSearchParams('foo=bar'), + }) + + const { url } = useOgImage({ + searchParams: { baz: 'qux' }, + }) + + expect(url).toBe('http://localhost/about.png?foo=bar&baz=qux') + }) + + test('searchParams should override existing query variables', () => { + mockLocation.mockReturnValue({ + origin: 'http://localhost', + pathname: '/about', + searchParams: new URLSearchParams('foo=bar'), + }) + + const { url } = useOgImage({ + searchParams: { foo: 'baz' }, + }) + + expect(url).toBe('http://localhost/about.png?foo=baz') + }) + test('allows setting a custom extension', () => { mockLocation.mockReturnValue({ origin: 'http://localhost', @@ -166,10 +208,11 @@ describe('useOgImage', () => { width: 1024, height: 768, quality: 75, + searchParams: new URLSearchParams({ baz: 'qux' }), }) expect(url).toBe( - 'http://localhost/user/1.png?foo=bar&width=1024&height=768&quality=75', + 'http://localhost/user/1.png?foo=bar&baz=qux&width=1024&height=768&quality=75', ) expect(width).toBe(1024) expect(height).toBe(768) diff --git a/packages/ogimage-gen/src/hooks.ts b/packages/ogimage-gen/src/hooks.ts index 1b414daace85..e13af2988965 100644 --- a/packages/ogimage-gen/src/hooks.ts +++ b/packages/ogimage-gen/src/hooks.ts @@ -5,6 +5,7 @@ export type OgImageUrlOptions = { width?: number height?: number quality?: number + searchParams?: URLSearchParams | Record } export const OGIMAGE_DEFAULTS = { @@ -15,11 +16,20 @@ export const OGIMAGE_DEFAULTS = { } export const useOgImage = (options?: OgImageUrlOptions) => { - const { origin, pathname, searchParams } = useLocation() + const { origin, pathname, searchParams: locationSearchParams } = useLocation() const ext = options?.extension || OGIMAGE_DEFAULTS.extension const width = options?.width const height = options?.height const quality = options?.quality + const searchParams = + options?.searchParams instanceof URLSearchParams + ? options?.searchParams + : new URLSearchParams(options?.searchParams || {}) + const outputSearchParams = new URLSearchParams({ + ...Object.fromEntries(locationSearchParams), + ...Object.fromEntries(searchParams), + }) + const output = [origin] // special case if we're at the root, image is available at /index.ext @@ -32,18 +42,18 @@ export const useOgImage = (options?: OgImageUrlOptions) => { output.push(`.${ext}`) if (width) { - searchParams.append('width', width.toString()) + outputSearchParams.append('width', width.toString()) } if (height) { - searchParams.append('height', height.toString()) + outputSearchParams.append('height', height.toString()) } if (quality) { - searchParams.append('quality', quality.toString()) + outputSearchParams.append('quality', quality.toString()) } // only append search params if there are any, so we don't up with a trailing `?` - if (searchParams.size) { - output.push(`?${searchParams}`) + if (outputSearchParams.size) { + output.push(`?${outputSearchParams}`) } return {