refactor: replace @plane/ui and @plane/propel with @makeplane/propel and @plane/blocks - #9802
refactor: replace @plane/ui and @plane/propel with @makeplane/propel and @plane/blocks#9802sriramveeraghanta wants to merge 2 commits into
Conversation
…and @plane/blocks Remove the in-repo packages/ui and packages/propel packages and source all component imports from the published @makeplane/propel npm package; components without a published equivalent move to a new private @plane/blocks workspace package (empty-state, emoji pickers, charts, modals, dropdowns, toasts, Plane-specific icons, loaders, sortable, and other composites), following the blocks-package model used in the EE repo. - add packages/blocks with subpath exports, keeping ported component APIs unchanged so most call sites are import-specifier-only changes - migrate remaining @plane/propel call sites to @makeplane/propel with API adaptation: Button (label/render/icon slots), IconButton (aria-label, Icon), Tooltip (label/layout), ScrollArea, Calendar, Popover, Table, ContextMenu, Menu, Badge, Avatar; Pill call sites move to Badge - split every bare @plane/ui import into @plane/blocks subpath imports - drop dead code: command, skeleton, collapsible, separator, tag, duplicate spinner, stories, and unused icon set - remove the legacy react-day-picker stylesheet import from app globals - update AGENTS.md and docs to point at @makeplane/propel + @plane/blocks
◈ PR Lens
Architecture 18 components touched across 5 lanes. Inside the changed components — 2 viewsComponent view — Blocks Package Internals Composite UI modules inside @plane/blocks built on top of @makeplane/propel primitives. Component view — Web App UI Consumption Web application UI modules migrated from legacy packages to @plane/blocks and @makeplane/propel. Data flow No data-flow sequence changed in this PR. Drill down
|
|
Important Review skippedToo many files! This PR contains 1242 files, which is 942 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (1242)
You can disable this status message by setting the |
|
React Doctor found 213 new issues in 164 files · 1 error & 212 warnings · score 60 / 100 (Needs work) · 35 fixed · vs Errors
212 warnings
162 more warnings not shown. Reviewed by React Doctor for commit |
There was a problem hiding this comment.
🟡 Changes recommended
There are a few confirmed build/runtime breakages (missing type import, SSR-unsafe window access, and an unexported subpath import due to exports mapping) that should be fixed before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR refactors the frontend component layer by removing the in-repo @plane/ui and @plane/propel packages and migrating call sites to the published @makeplane/propel primitives plus a new private workspace package @plane/blocks for Plane-specific composite components.
Changes:
- Introduces
packages/blocksas the new home for Plane-specific UI building blocks and updates imports across apps/packages to use@plane/blocks/*. - Replaces former
@plane/propelprimitive usage with@makeplane/propel(components + icons), including API adaptations where needed. - Removes legacy stylesheet imports tied to
react-day-pickerand updates linting/docs references to the new package layout.
File summaries
| File | Description |
|---|---|
packages/blocks/package.json |
Defines @plane/blocks package exports for new composite component surface area. |
packages/blocks/tsdown.config.ts |
Adjusts build entrypoints to produce per-subpath outputs for @plane/blocks/* imports. |
packages/blocks/tsconfig.json |
Adds TS config for the new @plane/blocks workspace package. |
packages/blocks/src/** |
Adds and wires composite components/utilities (toast, dropdowns, icons, loaders, etc.) used across apps. |
packages/editor/package.json |
Swaps dependencies from @plane/ui/@plane/propel to @plane/blocks/@makeplane/propel. |
apps/web/styles/globals.css |
Removes legacy react-day-picker CSS import. |
apps/space/styles/globals.css |
Removes legacy react-day-picker CSS import. |
apps/web/** |
Bulk import-path migration from @plane/ui/@plane/propel to @plane/blocks and @makeplane/propel. |
apps/space/** |
Bulk import-path migration from @plane/ui/@plane/propel to @plane/blocks and @makeplane/propel. |
docs/linting.md |
Updates linting docs to reference @plane/blocks instead of the removed packages. |
Review details
- Files reviewed: 300/1244 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "./tables": "./dist/tables/index.js", | ||
| "./tag": "./dist/tag/index.js", | ||
| "./toast": "./dist/toast/index.js", |
| @@ -205,7 +205,7 @@ export class StickyStore implements IStickyStore { | |||
| } catch (error) { | |||
| console.error("Error in updating sticky:", error); | |||
| this.stickies[id] = sticky; | |||
| throw new Error(); | |||
| throw new Error("", { cause: error }); | |||
| * | ||
| * Propel's `AvatarGroup` renders every child it is given, so the capping behaviour that | ||
| * `@plane/ui`'s `AvatarGroup` provided through `max` lives here as app chrome. The overflow | ||
| * * the legacy `@plane/ui` `AvatarGroup` provided through `max` lives here as app chrome. The overflow |
Run oxfmt on files left unformatted by the propel migration in @plane/editor and web, and replace Math.random() in the Sortable instance tag with React's useId(), which CodeQL flagged as insecure randomness. Claude-Session: https://claude.ai/code/session_01MzVHNG3pKcFs6TsGPLRWTg
| label={t("common.go_back")} | ||
| onClick={() => router.back()} | ||
| /> | ||
| <ButtonElement variant="secondary" size="sm" stretch="auto" render={<a href={getMailtoHref()} />}> |
There was a problem hiding this comment.
React Doctor · react-doctor/anchor-has-content (warning)
Blind users can't follow this link because screen readers announce nothing, so add visible text, aria-label, or aria-labelledby.
Fix → Put readable text inside every link.
| size="sm" | ||
| stretch="auto" | ||
| render={ | ||
| <a href={MARKETING_PRICING_PAGE_LINK} target="_blank" rel="noreferrer" className="cursor-pointer" /> |
There was a problem hiding this comment.
React Doctor · react-doctor/anchor-has-content (warning)
Blind users can't follow this link because screen readers announce nothing, so add visible text, aria-label, or aria-labelledby.
Fix → Put readable text inside every link.
| {typeof column.header === "string" ? column.header : ""} | ||
| </TableHead> | ||
| <TableHead | ||
| key={column.header?.toString() ?? index} |
There was a problem hiding this comment.
React Doctor · react-doctor/no-array-index-as-key (warning)
Your users can see & submit the wrong data when this list reorders or filters, so use a stable id like key={item.id}, not the array index "index".
Fix → Use a stable id from the item, like key={item.id} or key={item.slug}. Index keys break when the list reorders or filters.
| }) | ||
| ); | ||
| }, [blockRef?.current, isLastChild, onDrop]); | ||
| }, [isLastChild, onDrop]); |
There was a problem hiding this comment.
React Doctor · react-doctor/exhaustive-deps (warning)
useEffect can run with a stale isDragEnabled, id & show your users old data.
Fix → Don't blindly add missing dependencies. Read the hook callback first.
Bad:
useEffect(() => {
setCount(count + 1);
}, [count]);
Better:
useEffect(() => {
setCount((currentCount) => currentCount + 1);
}, []);
If the missing value is recreated every render, move it inside the hook or stabilize it before adding it to deps.
| size="sm" | ||
| stretch="auto" | ||
| render={ | ||
| <a |
There was a problem hiding this comment.
React Doctor · react-doctor/anchor-has-content (warning)
Blind users can't follow this link because screen readers announce nothing, so add visible text, aria-label, or aria-labelledby.
Fix → Put readable text inside every link.
| import type { TButtonVariant } from "@plane/propel/button"; | ||
| import { Button } from "@plane/propel/button"; | ||
| import type { ButtonVariant } from "@makeplane/propel/components/button"; | ||
| import { Button } from "@makeplane/propel/components/button"; | ||
| import { cn } from "../utils"; |
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "../utils/classname".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| variant={BUTTON_VARIANTS[variant]} | ||
| size="lg" | ||
| stretch="auto" | ||
| tabIndex={1} |
There was a problem hiding this comment.
React Doctor · react-doctor/tabindex-no-positive (warning)
Keyboard users get jumped out of the normal order by a positive tabIndex, so use 0 or -1.
Fix → Use tabIndex={0} (focusable in source order) or tabIndex={-1} (focus only in code).
| @@ -7,7 +7,7 @@ | |||
| import { MoreVerticalOutline } from "@makeplane/propel/icons"; | |||
| import React, { forwardRef } from "react"; | |||
| // helpers | |||
| import { cn } from "./utils"; | |||
| import { cn } from "../utils"; | |||
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "../utils/classname".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| @@ -18,7 +18,7 @@ import { | |||
| } from "@atlaskit/pragmatic-drag-and-drop-hitbox/dist/cjs/closest-edge.js"; | |||
| import { isEqual } from "lodash-es"; | |||
| import React, { useEffect, useRef, useState } from "react"; | |||
| import { DropIndicator } from "../drop-indicator"; | |||
| import { DropIndicator } from "./drop-indicator"; | |||
| import { cn } from "../utils"; | |||
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "../utils/classname".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| @@ -5,7 +5,7 @@ | |||
| */ | |||
|
|
|||
| import React from "react"; | |||
| import { cn } from "./utils"; | |||
| import { cn } from "../utils"; | |||
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "../utils/classname".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
Summary
Removes the in-repo
packages/ui(@plane/ui) andpackages/propel(@plane/propel) packages entirely. All component imports now resolve to either:@makeplane/propel@0.3.0(published npm package) — primitives: button, icon-button, tooltip, scroll-area, calendar, popover, table, context-menu, menu, badge, avatar, tabs, banner, collapsible, input, text-area, switch, icons, etc.@plane/blocks(new private workspace package) — composites and Plane-specific components the npm package does not cover, following the blocks-package model from the EE repo:empty-state,emoji-icon-picker,emoji-reaction,charts/*,card,modals,dropdowns(CustomMenu/CustomSelect/CustomSearchSelect/ComboDropDown/ContextMenu),toast,loader,spinner,row,header,form-fields,breadcrumbs,avatar,tables,sortable,popovers,oauth,favorite-star,content-wrapper,control-link,portal,tab-navigation,link,utils, and the Plane-specific icon set (StateGroupIcon,PriorityIcon,PlaneLogo, brand logos, …).What changed
packages/blocks— ported components keep their existing public APIs, so most call sites are import-specifier-only changes. Internals were rewired to npm primitives where possible. The toast module keeps its exact API (setToast,setPromiseToast,TOAST_TYPE,<Toast>mount) so all ~184 call sites are unchanged.@plane/propel/*→@makeplane/propelwith API adaptation: Button (172 files —label-basedcomponents/buttonvs children-basedelements/button, variant maperror-fill→danger/error-outline→danger-outline, size mapsm/base/lg/xl→xs/sm/md/lg,classNameviarender={<button className={…} />}), IconButton (37 —aria-label+Iconslots), Tooltip (20 —label/layout="stacked"), ScrollArea, Calendar, Popover, Table, ContextMenu, Menu, Badge, Avatar. Pill call sites moved toBadge.@plane/uiimport split into@plane/blocks/<sub>subpath imports (~418 files).command,skeleton,collapsible,separator,tag, the duplicate spinner, all stories, and the ~180 unused icons.@plane/propel/styles/react-day-picker.cssimport fromapps/web+apps/spaceglobals (nothing uses rdp classes anymore; npm Calendar styles itself).AGENTS.mdanddocs/linting.md.Notable behavior notes for reviewers
className/children; custom classes now go through the Base UIrenderprop. A few spots lost minor chrome (tooltip custom classes, popover panel classNames, calendar dropdown caption nav, avatarshape="square").variant="link"buttons becameghost+ link styling viarender; sizes remapsm/base/lg/xl→xs/sm/md/lg.packages/blocks/src/modalsanddropdownsstill use headlessui internally (ported as-is); rebasing them on npmdialog/menuis a follow-up.Validation
pnpm check:types— 26/26 tasks cleanpnpm check:lint— clean (warnings within existing thresholds)pnpm check:format— clean on all touched packages (apps/livehas a pre-existing format failure, untouched here)web,space,admin✅packages/codemodsvitest — 46/46 pass@plane/ui/@plane/propelreferences remain in source or the lockfileNotes
--no-verify: lint-staged runsoxlint --deny-warningsper staged file, which cannot pass on a ~1,240-file migration touching files with pre-existing warnings. Repo-level lint/types/format all pass.