refactor: centralize isExternalUrl check into ImageClient#1865
refactor: centralize isExternalUrl check into ImageClient#1865
Conversation
adriangohjw
left a comment
There was a problem hiding this comment.
added some UI comments in chromatic that need to look into
There was a problem hiding this comment.
(unrelated to this PR but realized this when reviewing the PR)
we should propably shift ImageClient into components/internal instead of currently components/complex/Image since the current location might be misleading that it should only be rendered by other components directly and should only be done so via Image.tsx, which is incorrect
ned this so that hte first child will have a spacing
There was a problem hiding this comment.
Pull request overview
This PR centralizes the repeated isExternalUrl check and assetsBaseUrl prefixing pattern into ImageClient itself, eliminating ~105 lines of duplicated URL-construction logic across 14+ components. Previously, every consuming component manually computed a imgSrc or backgroundSrc variable before passing it to ImageClient; now they simply pass the raw src and ImageClient handles the prefix logic internally.
Changes:
- Moved URL-construction logic (
isExternalUrl(src) || assetsBaseUrl === undefined ? src : \${assetsBaseUrl}${src}`) intoImageClient.tsxand relocated it fromcomplex/Image/to a newinternal/ImageClient/` directory - Removed inline URL-prefixing code from all 14 components listed in the PR description, and removed
ImageClientfrom thecomplex/Image/index.tsre-export - Updated
ChildrenPagesBoxLayoutto useInfoCardWithImage/InfoCardNoImagecomponents for consistency, and added abreak-wordsstyle toInfoCardscard text container
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
ImageClient/ImageClient.tsx |
Core change: adds isExternalUrl check and URL prefixing logic |
ImageClient/index.ts |
New barrel export for the relocated ImageClient |
complex/Image/index.ts |
Removes ImageClient re-export (breaking all downstream imports) |
complex/Image/Image.tsx |
Updates to import ImageClient from its new internal location |
InfoCards/common.ts |
Adds break-words to cardTextContainer slot |
InfoCards/InfoCards.stories.tsx |
Adds a story card with an excessively long single-word title |
InfoCards/InfoCardImage.tsx |
Removes inline URL construction, passes raw imageUrl |
ChildrenPages/ChildrenPages.tsx |
Removes ChildpageImage wrapper; BoxLayout now uses InfoCardWithImage/InfoCardNoImage |
Blockquote.tsx |
Passes raw imageSrc to ImageClient |
CollectionBlock.tsx |
Passes raw imageSrc to ImageClient |
Contentpic.tsx |
Passes raw imageSrc to ImageClient |
Hero/HeroBlock.tsx |
Passes raw backgroundUrl to ImageClient |
Hero/HeroFloating.tsx |
Passes raw backgroundUrl to ImageClient |
Hero/HeroGradient.tsx |
Passes raw backgroundUrl to ImageClient |
Hero/HeroSearchbar.tsx |
Passes raw backgroundUrl to ImageClient |
Hero/HeroLargeImage/HeroLargeImage.tsx |
Passes raw backgroundUrl + assetsBaseUrl to ImageContainer |
Hero/HeroLargeImage/ImageContainer.tsx |
Adds assetsBaseUrl prop, forwards to ImageClient |
LogoCloud.tsx |
Removes transformedSrc logic, passes raw src |
Navbar.tsx |
Passes raw logoUrl to imageClientProps |
processCollectionItems.ts |
Passes raw image?.src as imageSrc |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/components/src/templates/next/components/complex/Image/index.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Security review completed for this PR.
No high-confidence vulnerabilities found in the introduced changes.
What I checked in this diff:
- Injection vectors (XSS/template/path/command/SQL style sinks)
- Authn/authz and permission-boundary regressions
- Secret/token exposure and insecure logging
- SSRF/request-forgery/deserialization patterns
- Dependency/supply-chain changes
Notes:
- The main code movement centralizes image URL normalization into
ImageClient; I did not find a new exploitable path introduced by this refactor. - No dependency additions or version bumps were introduced in this PR.
Residual risk:
- Security behavior still depends on existing
isExternalUrlsemantics and upstream validation of user-controlled URLs, but this PR does not materially expand that risk surface.
adriangohjw
left a comment
There was a problem hiding this comment.
LGTM, thanks!
paiseh my other merged stuff caused more merge conflicts haha
There was a problem hiding this comment.
Automated Risk Assessment
Risk level: Medium-High
Code review required: Yes
Self-approval: No
Assessment based on the actual diff (not PR description/comments):
- Centralizes URL transformation logic into a shared
ImageClientused by many production components (Blockquote,Hero*,InfoCards,Navbar,Collectionutils, etc.), increasing blast radius of any regression. - Refactors
ChildrenPagesBoxLayoutto useInfoCardWithImage/InfoCardNoImage, which is a meaningful UI/UX behavior/layout change in a user-facing flow. - Cross-file behavioral changes span many frontend surfaces (26 files), so this is beyond a narrowly scoped low-risk patch.
Reviewer action taken:
- Requested additional reviewer:
@dcshzj(historical contributor on touched codepaths viagit log/git blame).
Notes:
CODEOWNERSis configured globally in this repository, so this automation is not auto-approving here.- PR already has an approval from
@adriangohjw; this automation did not add another approval.
79e467f to
c44f58c
Compare
reasoning: it's the current default for index pages -> makes more sense for continuity
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Merge activity
|
## Problem Closes ISOM-2183 The `isExternalUrl` check and `assetsBaseUrl` prefixing pattern was duplicated across **14+ files**. Every component that used `ImageClient` manually constructed the URL with the same pattern: ```typescript const imgSrc = isExternalUrl(src) || assetsBaseUrl === undefined ? src : \`\${assetsBaseUrl}\${src}\` ``` This caused: - Code duplication across multiple components - Potential for inconsistent URL handling - Risk of double-prefixing issues ## Solution Moved the URL construction logic into `ImageClient` itself. Now consuming components simply pass raw `src` URLs and the prefixing is handled centrally. **Breaking Changes** - [ ] Yes - this PR contains breaking changes - [x] No - this PR is backwards compatible **Improvements**: - Centralized URL prefixing logic into `ImageClient.tsx` - single source of truth - Removed ~105 lines of duplicated code across 14 components - Simplified component APIs - consumers now pass raw URLs - Updated `ChildrenPages` box layout to use `InfoCardWithImage` component for consistency **Files Updated**: | Component | Change | |-----------|--------| | `ImageClient.tsx` | Added `isExternalUrl` check and URL construction | | `ChildrenPages.tsx` | Removed `ChildpageImage` wrapper, uses `InfoCardWithImage` for box layout | | `InfoCardImage.tsx` | Removed `imgSrc` construction | | `Navbar.tsx` | Pass raw `logoUrl` | | `LogoCloud.tsx` | Removed `transformedSrc` logic | | `HeroFloating.tsx` | Pass raw `backgroundUrl` | | `HeroGradient.tsx` | Pass raw `backgroundUrl` | | `HeroSearchbar.tsx` | Pass raw `backgroundUrl` | | `HeroBlock.tsx` | Pass raw `backgroundUrl` | | `HeroLargeImage.tsx` | Pass raw `backgroundUrl`, updated `ImageContainer` props | | `ImageContainer.tsx` | Added `assetsBaseUrl` prop | | `Blockquote.tsx` | Pass raw `imageSrc` | | `CollectionBlock.tsx` | Pass raw `imageSrc` | | `Contentpic.tsx` | Pass raw `imageSrc` | | `Image.tsx` | Pass raw `src` | | `processCollectionItems.ts` | Pass raw `image?.src` | ## Before & After Screenshots **BEFORE**: N/A - This is a refactoring with no visual changes. **AFTER**: N/A - Functionality should be identical. ## Tests - [x] `npm run typecheck` - Passes - [ ] Visual verification in Storybook for: ChildrenPages, InfoCards, Hero variants, Blockquote, CollectionBlock, Contentpic, LogoCloud, Navbar **New scripts**: None **New dependencies**: None **New dev dependencies**: None
## Problem Closes ISOM-2183 The `isExternalUrl` check and `assetsBaseUrl` prefixing pattern was duplicated across **14+ files**. Every component that used `ImageClient` manually constructed the URL with the same pattern: ```typescript const imgSrc = isExternalUrl(src) || assetsBaseUrl === undefined ? src : \`\${assetsBaseUrl}\${src}\` ``` This caused: - Code duplication across multiple components - Potential for inconsistent URL handling - Risk of double-prefixing issues ## Solution Moved the URL construction logic into `ImageClient` itself. Now consuming components simply pass raw `src` URLs and the prefixing is handled centrally. **Breaking Changes** - [ ] Yes - this PR contains breaking changes - [x] No - this PR is backwards compatible **Improvements**: - Centralized URL prefixing logic into `ImageClient.tsx` - single source of truth - Removed ~105 lines of duplicated code across 14 components - Simplified component APIs - consumers now pass raw URLs - Updated `ChildrenPages` box layout to use `InfoCardWithImage` component for consistency **Files Updated**: | Component | Change | |-----------|--------| | `ImageClient.tsx` | Added `isExternalUrl` check and URL construction | | `ChildrenPages.tsx` | Removed `ChildpageImage` wrapper, uses `InfoCardWithImage` for box layout | | `InfoCardImage.tsx` | Removed `imgSrc` construction | | `Navbar.tsx` | Pass raw `logoUrl` | | `LogoCloud.tsx` | Removed `transformedSrc` logic | | `HeroFloating.tsx` | Pass raw `backgroundUrl` | | `HeroGradient.tsx` | Pass raw `backgroundUrl` | | `HeroSearchbar.tsx` | Pass raw `backgroundUrl` | | `HeroBlock.tsx` | Pass raw `backgroundUrl` | | `HeroLargeImage.tsx` | Pass raw `backgroundUrl`, updated `ImageContainer` props | | `ImageContainer.tsx` | Added `assetsBaseUrl` prop | | `Blockquote.tsx` | Pass raw `imageSrc` | | `CollectionBlock.tsx` | Pass raw `imageSrc` | | `Contentpic.tsx` | Pass raw `imageSrc` | | `Image.tsx` | Pass raw `src` | | `processCollectionItems.ts` | Pass raw `image?.src` | ## Before & After Screenshots **BEFORE**: N/A - This is a refactoring with no visual changes. **AFTER**: N/A - Functionality should be identical. ## Tests - [x] `npm run typecheck` - Passes - [ ] Visual verification in Storybook for: ChildrenPages, InfoCards, Hero variants, Blockquote, CollectionBlock, Contentpic, LogoCloud, Navbar **New scripts**: None **New dependencies**: None **New dev dependencies**: None
)" This reverts commit 56229e2.
* Revert "refactor: update RowLayout styling to match InfoCards design (#1891)" This reverts commit 30209a1. * Revert "feat(components): support contain image variant for child pages (#1889)" This reverts commit 3e003a3. * Revert "refactor: centralize isExternalUrl check into ImageClient (#1865)" This reverts commit 56229e2.
## Problem Closes ISOM-2183 The `isExternalUrl` check and `assetsBaseUrl` prefixing pattern was duplicated across **14+ files**. Every component that used `ImageClient` manually constructed the URL with the same pattern: ```typescript const imgSrc = isExternalUrl(src) || assetsBaseUrl === undefined ? src : \`\${assetsBaseUrl}\${src}\` ``` This caused: - Code duplication across multiple components - Potential for inconsistent URL handling - Risk of double-prefixing issues ## Solution Moved the URL construction logic into `ImageClient` itself. Now consuming components simply pass raw `src` URLs and the prefixing is handled centrally. **Breaking Changes** - [ ] Yes - this PR contains breaking changes - [x] No - this PR is backwards compatible **Improvements**: - Centralized URL prefixing logic into `ImageClient.tsx` - single source of truth - Removed ~105 lines of duplicated code across 14 components - Simplified component APIs - consumers now pass raw URLs - Updated `ChildrenPages` box layout to use `InfoCardWithImage` component for consistency **Files Updated**: | Component | Change | |-----------|--------| | `ImageClient.tsx` | Added `isExternalUrl` check and URL construction | | `ChildrenPages.tsx` | Removed `ChildpageImage` wrapper, uses `InfoCardWithImage` for box layout | | `InfoCardImage.tsx` | Removed `imgSrc` construction | | `Navbar.tsx` | Pass raw `logoUrl` | | `LogoCloud.tsx` | Removed `transformedSrc` logic | | `HeroFloating.tsx` | Pass raw `backgroundUrl` | | `HeroGradient.tsx` | Pass raw `backgroundUrl` | | `HeroSearchbar.tsx` | Pass raw `backgroundUrl` | | `HeroBlock.tsx` | Pass raw `backgroundUrl` | | `HeroLargeImage.tsx` | Pass raw `backgroundUrl`, updated `ImageContainer` props | | `ImageContainer.tsx` | Added `assetsBaseUrl` prop | | `Blockquote.tsx` | Pass raw `imageSrc` | | `CollectionBlock.tsx` | Pass raw `imageSrc` | | `Contentpic.tsx` | Pass raw `imageSrc` | | `Image.tsx` | Pass raw `src` | | `processCollectionItems.ts` | Pass raw `image?.src` | ## Before & After Screenshots **BEFORE**: N/A - This is a refactoring with no visual changes. **AFTER**: N/A - Functionality should be identical. ## Tests - [x] `npm run typecheck` - Passes - [ ] Visual verification in Storybook for: ChildrenPages, InfoCards, Hero variants, Blockquote, CollectionBlock, Contentpic, LogoCloud, Navbar **New scripts**: None **New dependencies**: None **New dev dependencies**: None



Problem
Closes ISOM-2183
The
isExternalUrlcheck andassetsBaseUrlprefixing pattern was duplicated across 14+ files. Every component that usedImageClientmanually constructed the URL with the same pattern:This caused:
Solution
Moved the URL construction logic into
ImageClientitself. Now consuming components simply pass rawsrcURLs and the prefixing is handled centrally.Breaking Changes
Improvements:
ImageClient.tsx- single source of truthChildrenPagesbox layout to useInfoCardWithImagecomponent for consistencyFiles Updated:
ImageClient.tsxisExternalUrlcheck and URL constructionChildrenPages.tsxChildpageImagewrapper, usesInfoCardWithImagefor box layoutInfoCardImage.tsximgSrcconstructionNavbar.tsxlogoUrlLogoCloud.tsxtransformedSrclogicHeroFloating.tsxbackgroundUrlHeroGradient.tsxbackgroundUrlHeroSearchbar.tsxbackgroundUrlHeroBlock.tsxbackgroundUrlHeroLargeImage.tsxbackgroundUrl, updatedImageContainerpropsImageContainer.tsxassetsBaseUrlpropBlockquote.tsximageSrcCollectionBlock.tsximageSrcContentpic.tsximageSrcImage.tsxsrcprocessCollectionItems.tsimage?.srcBefore & After Screenshots
BEFORE: N/A - This is a refactoring with no visual changes.
AFTER: N/A - Functionality should be identical.
Tests
npm run typecheck- PassesNew scripts: None
New dependencies: None
New dev dependencies: None