Skip to content

refactor: centralize isExternalUrl check into ImageClient#1865

Merged
seaerchin merged 19 commits intomainfrom
chin/isom-2183-update-child-pages-block-to-look-like-cards
Mar 19, 2026
Merged

refactor: centralize isExternalUrl check into ImageClient#1865
seaerchin merged 19 commits intomainfrom
chin/isom-2183-update-child-pages-block-to-look-like-cards

Conversation

@seaerchin
Copy link
Copy Markdown
Contributor

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:

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
  • 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

  • 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

@seaerchin seaerchin requested a review from a team as a code owner March 9, 2026 11:28
@linear
Copy link
Copy Markdown

linear bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

@adriangohjw adriangohjw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added some UI comments in chromatic that need to look into

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call

ned this so that hte first child will have a spacing
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}`) into ImageClient.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 ImageClient from the complex/Image/index.ts re-export
  • Updated ChildrenPages BoxLayout to use InfoCardWithImage/InfoCardNoImage components for consistency, and added a break-words style to InfoCards card 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.

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 isExternalUrl semantics and upstream validation of user-controlled URLs, but this PR does not materially expand that risk surface.

Open in Web View Automation 

Copy link
Copy Markdown
Contributor

@adriangohjw adriangohjw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

paiseh my other merged stuff caused more merge conflicts haha

@cursor cursor bot requested a review from dcshzj March 10, 2026 07:54
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ImageClient used by many production components (Blockquote, Hero*, InfoCards, Navbar, Collection utils, etc.), increasing blast radius of any regression.
  • Refactors ChildrenPages BoxLayout to use InfoCardWithImage / 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 via git log/git blame).

Notes:

  • CODEOWNERS is 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.

Open in Web View Automation 

@seaerchin seaerchin enabled auto-merge (squash) March 10, 2026 09:30
@seaerchin seaerchin disabled auto-merge March 10, 2026 09:37
@seaerchin seaerchin force-pushed the chin/isom-2183-update-child-pages-block-to-look-like-cards branch from 79e467f to c44f58c Compare March 16, 2026 06:23
reasoning: it's the current default for index pages -> makes more sense
for continuity
Copy link
Copy Markdown
Contributor Author

seaerchin commented Mar 19, 2026

@seaerchin seaerchin merged commit 56229e2 into main Mar 19, 2026
18 of 22 checks passed
Copy link
Copy Markdown
Contributor Author

Merge activity

@seaerchin seaerchin deleted the chin/isom-2183-update-child-pages-block-to-look-like-cards branch March 19, 2026 05:56
seaerchin added a commit that referenced this pull request Mar 19, 2026
## 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
seaerchin added a commit that referenced this pull request Mar 19, 2026
## 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
seaerchin added a commit that referenced this pull request Mar 19, 2026
seaerchin added a commit that referenced this pull request Mar 19, 2026
* 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.
seaerchin added a commit that referenced this pull request Mar 19, 2026
## 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants