refactor: replace @plane/ui and @plane/propel with @makeplane/propel and @plane/blocks - #9802
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 — Composite Blocks Package Composite UI components and layout blocks inside @plane/blocks backed by Propel primitives. Component view — Web App UI Architecture Plane web application feature views and components migrated to @plane/blocks and Propel primitives. Data flow No data-flow sequence changed in this PR. View
Tip The CLI's 🪧 More tips
Thanks for using PR Lens! It's built by Coldtea, free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
WalkthroughWarning Review details and warnings were omitted to fit the comment limit. |
|
React Doctor found 82 new issues in 70 files · 82 warnings · score 71 / 100 (Needs work) · 210 fixed · vs 82 warnings
32 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.
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
|
@coderabbitai skip all the import update changes and focus on reviewing the rest of the files. |
|
✏️ Learnings added
|
Modified the handleClose function in the ContextMenu component to utilize React's useCallback for better performance and to prevent unnecessary re-renders. Additionally, removed the tag export from package.json as it is no longer needed.
…ane/propel to 0.7.3 Takes preview's lockfile and re-resolves it against this branch's manifests. Bumps the @makeplane/propel catalog pin 0.3.0 -> 0.7.3 and replaces the @base-ui-components/react beta with @base-ui/react 1.6.0, overridden so propel and @plane/blocks share one Base UI copy.
- sticky store: give the rethrown update error a message (keeps cause) - gantt/label DnD HOCs: list every value the drag registration reads; rename the shadowed element/instruction bindings - calendar day tile: read issues/handleDragAndDrop through useEffectEvent so drops never act on a stale map without re-registering the target; the mobile day cell is a real button - insight table loader: stable skeleton row/column keys - webhook form: drop the render override that hid the submit type
- blocks: add @plane/i18n, @tanstack/react-virtual, class-variance-authority,
date-fns deps; vitest, jsdom, @testing-library/{react,user-event} devDeps;
test script running the unit and dom vitest projects
- blocks: vitest.config.ts (unit + dom projects), vitest.setup.dom.ts jsdom
shims and the dom-scrolling guard test ported from EE
- blocks: tsdown entry for src/emoji-icon-picker/data.ts (no-op until F6)
- catalog: jsdom 23.2.0, @testing-library/react 16.3.3,
@testing-library/user-event 14.6.1
- web: depend on @base-ui/react
Non-blocks prerequisites (F1) for the Propel-based @plane/blocks port:
- @plane/hooks: port useDialogDismissal from EE (Escape closes only the
topmost dialog; optional outside-click handler) and export it.
- @plane/utils: add getAvatarName (drops "(you)"-style parenthetical
tokens before Avatar initials are derived).
- @plane/i18n: add 13 keys across all 21 locales:
aria_labels.{common.more_actions,common.remove,color_picker.open,
color_picker.swatches,color_picker.hex_input,breadcrumb.trail,
breadcrumb.show_more} and common.actions.add_reaction, searching,
no_matching_labels, adding_label, add_label_to_labels, labels_count.
Values for the 19 locales shared with EE are reused verbatim from EE;
ka-ge and nl are machine_translated and need native review.
…and spinner from EE - Port EE types/, utils/ (placement + DotSeparator), skeleton/, pill-button/ and virtual-list/ with the CE AGPL header; spinner keeps CE rendering and switches its index to EE's named exports. - Export the new ./pill-button, ./skeleton, ./types and ./virtual-list subpaths alongside the legacy ones. - Switch every legacy blocks file (except card/ and charts/) to import cn from @plane/utils. - Keep a deprecated cn re-export in @plane/blocks/utils until the apps/web importers move to @plane/utils (F5).
…m charts - Port EE card/ (string-union variant/spacing/direction instead of enums) and portal/ (PortalWidth/PortalPosition unions, onOutsideClick via useDialogDismissal) and update their call sites: profile overview stats/workload, the dev error page and the analytics work-items modal. - Add the new ./layout (Row, Header, ContentWrapper, ControlLink), ./auth (OAuthOptions, PasswordInput, PasswordStrengthIndicator) and ./common (ColorPicker, ColorSwatchPicker, DragHandle, DropIndicator, FavoriteStar, Sortable, toSideAndAlign) subpaths alongside the legacy ones, with the EE color-swatch-picker tests. - Charts keep the CE recharts-2 files: cn now comes from @plane/utils, the tooltips pass spacing="sm", and the unused scatter-chart is removed.
Every blocks file now imports cn from @plane/utils, so the local copy and its tailwind-merge dependency have no users left (target-blocks F2, §4).
…pter The ported Sortable imported monitorForElements from the CJS build while its Draggable rows register on the ESM entry point, so under Vite the monitor listened on a separate adapter instance and never saw a drop. Import both from @atlaskit/pragmatic-drag-and-drop/element/adapter and scope the monitor to drags that started in the same list, since the shared adapter now also reports every other element drag in the app. Adds dom tests for the monitor.
Port the EE versions of CE's icon set plus LabelPropertyIcon into @plane/blocks/icons with a hand-written index. Delete icon-wrapper and PlaneNewIcon; the app rail now uses Propel's Projects glyph. PriorityIcon no longer takes size/withContainer/containerClassName, so the 11 call sites that passed them now size the glyph with className.
Replace the legacy Base UI toast with EE's toast-manager and PlaneToastProvider (plus the toast-manager unit tests). Mount PlaneToastProvider inside TranslationProvider in web, space and admin; admin gains @plane/blocks and @plane/i18n. Create-issue toast actions become data via useCreateIssueToastActions. A transitional TOAST_TYPE const keeps call sites compiling until the literal codemod lands in the next commit.
…t into blocks Codemod every TOAST_TYPE.X to its string literal and drop TOAST_TYPE from the @plane/blocks/toast imports across web, space and admin (EE parity, critic C17). Admin's local @/providers/toast adapter is deleted and its importers now use @plane/blocks/toast. Removes the transitional TOAST_TYPE shim from blocks.
…egacy dirs
F5 path-rename codemod (target-blocks.md §6) over apps/web, apps/space and
packages/editor:
- @plane/blocks/loader -> @plane/blocks/skeleton
- row/header/content-wrapper/control-link -> @plane/blocks/layout (merged)
- sortable/favorite-star -> @plane/blocks/common
- oauth and form-fields PasswordInput/PasswordStrengthIndicator -> @plane/blocks/auth
- cn from @plane/blocks/utils -> @plane/utils
- TPlacement from @plane/blocks/utils/placement -> TPopoverMenuPlacement
from @plane/blocks/common
- Propel PillButton -> Pill in the 6 applied-filter files (EE-twin shape)
Delete blocks src/{loader,row,header,content-wrapper,control-link,sortable,
favorite-star,oauth}, their exports, the form-fields password files and the
temporary cn re-export in utils. Widen the legacy dropdowns Placement type
with auto* (it is passed straight to usePopper) now that the quick-action
placement type resolves.
- Replace the legacy emoji-icon-picker with the EE Propel port (rich popover, Propel tabs, lifted search, i18n) and add the data-only `@plane/blocks/emoji-icon-picker/data` subpath - Port EE emoji-reaction with animated-counter moved inside it; delete the unexported src/animated-counter - Port the EE empty-state directory (lazy illustration registry, action-button on Propel Button) including all illustrations - Port EE emoji-picker and emoji-reaction-picker dom tests - Call sites: EmojiIconPickerTypes.EMOJI/ICON -> "emoji"/"icon" and TChangeHandlerProps -> ChangeHandlerPayload
Add @plane/blocks/dialog (ConfirmDialog on Propel AlertDialog) and @plane/blocks/context-menu (parentRef-driven ContextMenu on Propel context-menu parts) from EE. CE change: the leaf context-menu row passes `description` to Propel's native second line instead of the trailing slot; the submenu trigger keeps trailing. Add CE tests: context-menu helpers (unit), ContextMenu open/anchoring, row rendering, danger/disabled/description, nested submenus (dom), and ConfirmDialog open/close, labels, confirm/cancel, submitting veto and variants (dom). The legacy dropdowns ContextMenu stays exported until cleanup.
Add @plane/blocks/select (Select + Select.Trigger/Value on Base UI combobox and Propel combobox parts, virtualized and infinite lists, SelectTriggerChrome, SelectListBody/MenuPanel/MenuRow, NestedSubmenu) and @plane/blocks/breadcrumb (Breadcrumbs on Propel breadcrumb with a collapsed-crumb menu, BreadcrumbNavigationSelect on the new Select), ported together because select/trigger-chrome and breadcrumb import each other. Port the EE select, nested-submenu dismissal and breadcrumb dom tests. The legacy dropdowns and breadcrumbs subpaths stay exported until their importers move.
Add @plane/blocks/property-select with the CE subset of EE's typed pickers: CycleSelect, DateSelect, DateRangeSelect, EstimateSelect, IntakeStateSelect, LabelSelect, MemberSelect, ModuleSelect, PrioritySelect, ProjectSelect and StateSelect (EE-only pickers are not ported). All are presentational; app bindings supply getValues/value. CE change: every *SelectProps takes an optional tabIndex and forwards it to Select.Trigger (DateSelectCommonProps already had it), so forms that order focus explicitly keep that order. Port the EE date-select dom test and add a CE test that each picker puts tabIndex on its trigger.
Switch the 41 app importers of `Breadcrumbs` from the legacy @plane/blocks/breadcrumbs subpath to the EE-ported @plane/blocks/breadcrumb (same compound API). Drop the `onBack` prop at its 7 call sites (the collapsed-crumb overflow menu replaces the small- screen back arrow) and the router hook it left unused in 5 headers. The legacy ./breadcrumbs subpath now exports only BreadcrumbNavigationSearchDropdown, which its owning slices replace; its three private legacy crumb parts are inlined into it and the legacy Breadcrumbs file is deleted.
…timer The "turns a row with renderable nestedMenuItems into a submenu trigger" test moves the real pointer from the submenu trigger to a nested row. With user-event's default delay each step yields to timers, so on a loaded run (the full suite once the select/breadcrumb/property-select tests landed) the submenu's hover-out close fired first and the click hit a detached row. Run that test's pointer sequences with `delay: null`.
Port the EE dropdown bindings (state, intake-state, priority, member, cycle, module, estimate, project, label) onto @plane/blocks/property-select, rewritten on CE's MobX stores: each serves its store list as one page and filters the search client-side. Port EE's LayoutDropDown onto the blocks Select.
…ender useRef(Symbol()) evaluated Symbol() on every render and discarded it; a lazy useState initialiser creates one identity per mounted dialog.
Summary
Removes the in-repo
packages/ui(@plane/ui) andpackages/propel(@plane/propel) packages and rebuilds CE's UI layer on the published@makeplane/propel@0.7.3, following the model already shipped in the enterprise repo:The first revision of this PR ported the legacy
@plane/uicomposites into@plane/blocksas-is (headlessui modals/menus/selects, react-popper, tailwind-merge). This revision does the full rebuild: every composite is now built on Propel primitives, and Headless UI, react-popper, Popper, react-color, framer-motion and react-day-picker are gone from the workspace.@plane/blocks(final subpaths)auth,breadcrumb,card,charts/{area,bar,line,pie,radar}-chart,charts/tree-map,common,context-menu,dialog,emoji-icon-picker(+/data),emoji-reaction,empty-state,icons,layout,pill-button,portal,property-select,select,skeleton,spinner,toast,types,utils,virtual-list.cnfrom@plane/utils.apps/web/core/components/dropdowns/<x>/<x>-select.tsx.What changed
@base-ui/react@1.6.0(workspace override).ModalCore→ PropelDialog/DialogContent(size ladder xs 480 · sm 576 · md 672 · lg 800 · xl 1000 · full);AlertModalCore→@plane/blocks/dialogConfirmDialog(PropelAlertDialog). Outside-click/Escape dismissal kept per call site (CE parity).CustomMenu,PopoverMenu, headlessMenu→ PropelMenu;ContextMenu→@plane/blocks/context-menuon Propel.CustomSelect,CustomSearchSelect,ComboDropDown, headlessCombobox/Listbox+usePopper→@plane/blocks/select(Combobox-based engine: search, groups, multi-select, virtualisation, disabled rows) and@plane/blocks/property-select(state, priority, member, cycle, module, estimate, project, intake-state, label, date, date-range), bound to CE stores inapps/web/core/components/dropdowns/*.@plane/blocks/toastadapter over Propel's toast manager;<PlaneToastProvider>mounted in web, space and admin (admin's local toast copy removed);TOAST_TYPE.X→ string literals; JSXactionItems→ data (useCreateIssueToastActions).Loader→@plane/blocks/skeleton;Row/Header/ContentWrapper/ControlLink→@plane/blocks/layout;Breadcrumbs→@plane/blocks/breadcrumb;PillButton→Pill;AvatarGroupOverflow→ PropelAvatarGroup max; headlessDisclosure/Tab/Transition→ PropelCollapsible/Tabs/CSS; react-color pickers →ColorPicker/ColorSwatchPicker; blocksTable→ app-localDataTableon PropelTable.@plane/hooksuseOutsideClickDetectorskips Base UI portal popups (composedPath); newuseDialogDismissal;@plane/utilsgetAvatarName; 13 new i18n keys in all 21 locales.Button+ anchor (notAnchorButton, which is text-link chrome), no nested interactive elements, conflicting render-prop classes removed, accessible names everywhere.Accepted visual / behaviour changes (reviewers please eyeball)
The full per-file drift log from the migration is available on request.
Review threads
./tagexport (already resolved),sticky.store.tserror message,avatar-group-overflow.tsxdoc typo (file removed — PropelAvatarGroupowns overflow now).useEffectEvent), index keys, button types, placeholder-only fields, positive tabIndex, labelled controls, blocks barrel imports,anchor-has-content(anchors now carry their own label), and theno-effect-with-fresh-depserror in the legacy context menu (file removed).Validation
pnpm install --frozen-lockfile✅pnpm check(format, lint, types) — 56/56 ✅; lint warnings down vs the previous revision of this branch (web 743 → 553, blocks 50 → 18)web,space,admin✅@plane/blocks277/277,@plane/codemods33/33 ✅@plane/i18nsync-check: 3,856 keys in every locale ✅addlicense, same as CI) ✅preview): 0 new errors@plane/ui,@plane/propel,@headlessui/react,react-popper,react-colororTOAST_TYPEreferences remainNotes
@plane/blocksmodules are ports of the enterprise@plane/blockspackage and carry the CE AGPL header.nlandka-gestrings for the 13 new i18n keys are machine translations and need native review.--diffmode fails in this repo (catalog:dependency resolution in its base copy); the HEAD-vs-preview full-scan comparison was used instead.--no-verify: lint-staged runsoxlint --deny-warningsper staged file, which flags pre-existing warnings in touched files; package-level lint/types/format all pass.