Skip to content

feat(Icon)!: collapse duplicate share icons into one - #1191

Draft
DreaminDani wants to merge 1 commit into
mainfrom
cui-115-share-icon-confusion
Draft

feat(Icon)!: collapse duplicate share icons into one#1191
DreaminDani wants to merge 1 commit into
mainfrom
cui-115-share-icon-confusion

Conversation

@DreaminDani

@DreaminDani DreaminDani commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Why?

We shipped two icons that nobody could tell apart. share and popout were both a rounded square with an arrow leaving the top-right corner, so share read as "opens in a new tab" wherever it was used to mean "share". Our friends at Vercel flagged the confusion in #design-feedback, and design agreed to collapse down to one share icon.

The icon we actually want for sharing was the forward arrow, awkwardly registered as share-arrow. So this removes the lookalike and gives the survivor the obvious name.

How?

  • Deleted the old share glyph (the popout duplicate).
  • Renamed Share-Arrow.tsxShare.tsx, so share is now the forward-arrow glyph. share-network is unchanged and stays in the library.
  • Regenerated types.ts / IconsLight.ts / IconsDark.ts via yarn convert:regenerate --type=icons.
  • Added 'share-arrow': 'share' to ASSET_NAME_MAPPINGS.aliases and repointed the legacy ShareArrow deprecated entry, so existing share-arrow consumers keep rendering at runtime.
  • Fixed a latent codegen bug this surfaced — see below.
  • Changeset added.

⚠️ Two things for a reviewer to weigh in on

1. minor vs major. .llm/CONVENTIONS.md says breaking → major, and this is unambiguously breaking. But this package has never shipped a major, and a major on 0.11.0 cuts 1.0.0 — that felt like not my call to make. I went with minor (→ 0.12.0), which is the conventional way to express a breaking change pre-1.0. Happy to switch it to major if you'd rather; just say so.

2. I fixed a bug I didn't set out to fix. getComponentFiles in .scripts/js/shared/svg-converter-utils.mjs matched every .tsx in the asset directories, so the colocated Loaders.stories.tsx was treated as an asset. The first regenerate emitted:

import Loaders.stories from '../Loaders.stories';   // not valid TypeScript
'loaders.stories': Loaders.stories,

...and prettier then failed on the broken output. Nobody had regenerated since that stories file was added, so this was sitting there waiting for the next person. I excluded *.stories.tsx / *.test.tsx / *.spec.tsx. It's genuinely unrelated scope — happy to split it into its own PR if you'd prefer this one stay single-purpose, though note the icon change can't be regenerated without it.

Migration

  • Using share for a link that opens in a new tab? Switch to popout. This is the one case that will not fail loudly — Icon's name prop resolves through a Record<string, string> alias map, so name="share" keeps compiling and silently renders the new glyph.
  • Using share as a real "share with someone" affordance? No change needed, you get the forward arrow.
  • Using share-arrow? Rename to share. It's aliased so it still renders, but it's no longer in the IconName union, so strictly-typed props (Button.iconLeft, Link.icon, Dropdown.Item.icon, …) will fail typecheck until updated. That's intentional — it's how you find your call sites.

Ordering ⚠️ — this is why the PR is a draft

This must not merge before ClickHouse/control-plane#40485 and ClickHouse/control-plane#40490, which together sweep that repo clear of share usages. Because a stale name="share" can't fail loudly, merging this first would silently repaint a dozen docs links in the console with a share arrow. Undraft once both are in.

I checked the other consumers I have locally (clickhouse-docs, marketing-website, demo-ui, clickpipes-platform, hyperdx-ee) and none reference share or share-arrow. Residual risk is a repo I can't see.

Tickets?

Contribution checklist?

  • You've done enough research before writing
  • You have reviewed the PR
  • The commit messages are detailed
  • The build command runs locally
  • Assets or static content are linked and stored in the project
  • For documentation, guides or references, you've tested the commands

Security checklist?

  • All user inputs are validated and sanitized
  • No usage of dangerouslySetInnerHTML
  • Sensitive data has been identified and is being protected properly
  • Build output contains no secrets or API keys

Preview?

Verified locally:

check result
yarn typecheck exit 0
yarn lint 0 errors
yarn build passes; share appears once in dist types, share-arrow gone, only Share.js + Share-Network.js remain
alias resolution resolveAssetName('share-arrow') === 'share', resolveAssetName('ShareArrow') === 'share' (warns as deprecated), share-arrow absent from the registry, popout intact, no loaders.stories key
glyph render share is the forward arrow, unmistakably distinct from popout

Worth a look at the Assets/Icon → Icons Storybook gallery to confirm share and popout now read as different icons side by side.

🤖 Generated with Claude Code

CUI-115. The `share` glyph was a rounded square with an arrow leaving the
top-right corner, which made it visually indistinguishable from `popout` —
so it read as "opens in a new tab" wherever it was used to mean "share".
Vercel flagged the resulting confusion in #design-feedback.

Removes that glyph and renames the survivor: `share` is now the forward
arrow previously registered as `share-arrow`. `share-network` is unchanged.

`share-arrow` is aliased to `share` so it keeps rendering at runtime, but it
is no longer a member of the `IconName` union, so strictly-typed props fail
typecheck until updated. Note the one case that cannot fail loudly: a stale
`<Icon name="share" />` still compiles and silently renders the new glyph.
control-plane was swept clear of `share` usages before this lands.

Also fixes a latent codegen bug this surfaced: `getComponentFiles` matched
every `.tsx` in the asset directories, so `Loaders.stories.tsx` was picked up
as an asset and regeneration emitted a `loaders.stories` icon plus invalid
TypeScript. Storybook and test files are now excluded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: eef55e9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@clickhouse/click-ui Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@workflow-authentication-public

Copy link
Copy Markdown
Contributor

Storybook Preview Deployed

✅ Preview URL: https://click-bbski8ujb-clickhouse.vercel.app

Built from commit: 6fd704564c6aa075322bff70405efdd8e605a0ed

@DreaminDani
DreaminDani requested a review from XOP September 10, 2026 23:11
@XOP XOP added the on hold On hold until further notice, do not merge label Sep 11, 2026
exported `IconName` union, so strictly-typed props (`Button.iconLeft`,
`Link.icon`, `Dropdown.Item.icon`, …) will fail typecheck until updated.

Also fixes a latent bug in the asset codegen: `getComponentFiles` matched every

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

internal changes usually should be omitted in changeset, it's for evident library and assets updates. Infra, meta, env, docs work is not part of it.

'c#': 'c-sharp',
// CUI-115: the old `share` glyph duplicated `popout` and was removed; the
// surviving share glyph moved from `share-arrow` to `share`.
'share-arrow': 'share',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This line has no effect. The Icon component that consumers import never reads that map. Only an internal, unexported component does.

2 options:

  • to make it work in Icon.tsx, pass name through resolveAssetName before the registry lookup, then the changeset is true (see below)
  • delete this alias line and change changeset to "share-arrow is removed, rename it to share." Then the PR is purely mechanical. (easy but leaves old issue behind)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

more consistent approach - is to do the following:

  • Resolve the name once in the exported Icon. src/components/Icon/Icon.tsx
+import { resolveAssetName } from '@/components/Assets/config';
 
-const SvgImage = ({ name, size, theme, ...props }: ImageType) => {
+const SvgImage = ({ name: rawName, size, theme, ...props }: ImageType) => {
+  const name = resolveAssetName(rawName) as ImageName;
   if (Object.keys(FlagsLight).includes(name)) {
  • move the alias in the config from alias to deprecated. Then on consumer side the console will output meaningful message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

on hold On hold until further notice, do not merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants