From e67f0cf15d31b78c9f8ab28458a5e95572995b32 Mon Sep 17 00:00:00 2001 From: vickytechkey Date: Fri, 6 Nov 2026 17:24:20 +0530 Subject: [PATCH] fix(web,ui): fix hydration error from nested button descendants and MobX strict-mode warnings - Replace nested + ); } @@ -102,16 +101,15 @@ function BackgroundButton(props: ButtonProps) { layout="stacked" disabled={!showTooltip || isMobile} > - + ); } @@ -125,10 +123,9 @@ function TransparentButton(props: ButtonProps) { layout="stacked" disabled={!showTooltip || isMobile} > - + ); } diff --git a/apps/web/core/components/issues/issue-detail-widgets/sub-issues/issues-list/properties.tsx b/apps/web/core/components/issues/issue-detail-widgets/sub-issues/issues-list/properties.tsx index 04da15d3c..44558e3b9 100644 --- a/apps/web/core/components/issues/issue-detail-widgets/sub-issues/issues-list/properties.tsx +++ b/apps/web/core/components/issues/issue-detail-widgets/sub-issues/issues-list/properties.tsx @@ -47,9 +47,6 @@ export const SubIssuesListItemProperties = observer(function SubIssuesListItemPr // oxlint-disable-next-line unicorn/consistent-function-scoping const handleEventPropagation = (e: SyntheticEvent) => { e.stopPropagation(); - if (e.type !== "focus") { - e.preventDefault(); - } }; const handleStartDate = (date: Date | null) => { diff --git a/apps/web/core/components/issues/issue-layouts/kanban/block.tsx b/apps/web/core/components/issues/issue-layouts/kanban/block.tsx index e64e356c7..07d872f1d 100644 --- a/apps/web/core/components/issues/issue-layouts/kanban/block.tsx +++ b/apps/web/core/components/issues/issue-layouts/kanban/block.tsx @@ -87,7 +87,6 @@ const KanbanIssueDetailsBlock = observer(function KanbanIssueDetailsBlock(props: // oxlint-disable-next-line unicorn/consistent-function-scoping const handleEventPropagation = (e: React.MouseEvent) => { e.stopPropagation(); - e.preventDefault(); }; useOutsideClickDetector(menuActionRef, () => setIsMenuActive(false)); diff --git a/apps/web/core/components/sidebar/sidebar-item.tsx b/apps/web/core/components/sidebar/sidebar-item.tsx index fa3cfef1b..19289764d 100644 --- a/apps/web/core/components/sidebar/sidebar-item.tsx +++ b/apps/web/core/components/sidebar/sidebar-item.tsx @@ -22,7 +22,7 @@ interface AppSidebarItemData { } interface AppSidebarItemProps { - variant?: "link" | "button"; + variant?: "link" | "button" | "div"; item?: AppSidebarItemData; } @@ -47,6 +47,7 @@ interface AppSidebarButtonItemProps { onClick?: () => void; disabled?: boolean; className?: string; + as?: "button" | "div"; } // ============================================================================ @@ -107,7 +108,15 @@ function AppSidebarLinkItem({ href, children, className }: AppSidebarLinkItemPro ); } -function AppSidebarButtonItem({ children, onClick, disabled = false, className }: AppSidebarButtonItemProps) { +function AppSidebarButtonItem({ as: Component = "button", children, onClick, disabled = false, className }: AppSidebarButtonItemProps) { + if (Component === "div") { + return ( +
+ {children} +
+ ); + } + return ( + + )} diff --git a/apps/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx b/apps/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx index d87cb4f69..e0e0af498 100644 --- a/apps/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx +++ b/apps/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx @@ -191,8 +191,7 @@ export function FavoriteFolder(props: Props) { align="end" disabled={isDragging || isMobile} > - + +
diff --git a/apps/web/core/components/workspace/sidebar/help-section/root.tsx b/apps/web/core/components/workspace/sidebar/help-section/root.tsx index 6b23af1b2..7cadccf1c 100644 --- a/apps/web/core/components/workspace/sidebar/help-section/root.tsx +++ b/apps/web/core/components/workspace/sidebar/help-section/root.tsx @@ -32,7 +32,7 @@ export const HelpMenuRoot = observer(function HelpMenuRoot() { , isActive: isNeedHelpOpen, @@ -59,23 +59,11 @@ export const HelpMenuRoot = observer(function HelpMenuRoot() {
- - + toggleShortcutsListModal(true)}> + {t("keyboard_shortcuts")} - - + setProductUpdatesModalOpen(true)}> + {t("whats_new")} window.open("https://forum.plane.so", "_blank", "noopener,noreferrer")}>
diff --git a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx index 9d0fa86c0..db15fd7c6 100644 --- a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx +++ b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx @@ -313,8 +313,7 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem align="end" disabled={isDragging || isMobile} > - + +
)} <> diff --git a/apps/web/core/components/workspace/sidebar/user-menu-root.tsx b/apps/web/core/components/workspace/sidebar/user-menu-root.tsx index 95b78f56e..c5ea330db 100644 --- a/apps/web/core/components/workspace/sidebar/user-menu-root.tsx +++ b/apps/web/core/components/workspace/sidebar/user-menu-root.tsx @@ -59,7 +59,7 @@ export const UserMenuRoot = observer(function UserMenuRoot() { className="flex items-center" customButton={ { - if (rootStore?.user?.data?.id) this.currentUserId = rootStore?.user?.data?.id; - if (this.workspaceSlug !== rootStore.router.workspaceSlug) this.workspaceSlug = rootStore.router.workspaceSlug; - if (this.teamspaceId !== rootStore.router.teamspaceId) this.teamspaceId = rootStore.router.teamspaceId; - if (this.projectId !== rootStore.router.projectId) this.projectId = rootStore.router.projectId; - if (this.cycleId !== rootStore.router.cycleId) this.cycleId = rootStore.router.cycleId; - if (this.moduleId !== rootStore.router.moduleId) this.moduleId = rootStore.router.moduleId; - if (this.viewId !== rootStore.router.viewId) this.viewId = rootStore.router.viewId; - if (this.globalViewId !== rootStore.router.globalViewId) this.globalViewId = rootStore.router.globalViewId; - if (this.userId !== rootStore.router.userId) this.userId = rootStore.router.userId; - if (!isEmpty(rootStore?.state?.stateMap)) this.stateMap = rootStore?.state?.stateMap; - if (!isEmpty(rootStore?.state?.projectStates)) this.stateDetails = rootStore?.state?.projectStates; - if (!isEmpty(rootStore?.state?.workspaceStates)) this.workspaceStateDetails = rootStore?.state?.workspaceStates; - if (!isEmpty(rootStore?.label?.labelMap)) this.labelMap = rootStore?.label?.labelMap; - if (!isEmpty(rootStore?.memberRoot?.workspace?.workspaceMemberMap)) - this.workSpaceMemberRolesMap = rootStore?.memberRoot?.workspace?.memberMap || undefined; - if (!isEmpty(rootStore?.memberRoot?.memberMap)) this.memberMap = rootStore?.memberRoot?.memberMap || undefined; - if (!isEmpty(rootStore?.projectRoot?.project?.projectMap)) - this.projectMap = rootStore?.projectRoot?.project?.projectMap; - if (!isEmpty(rootStore?.module?.moduleMap)) this.moduleMap = rootStore?.module?.moduleMap; - if (!isEmpty(rootStore?.cycle?.cycleMap)) this.cycleMap = rootStore?.cycle?.cycleMap; + runInAction(() => { + if (rootStore?.user?.data?.id) this.currentUserId = rootStore?.user?.data?.id; + if (this.workspaceSlug !== rootStore.router.workspaceSlug) this.workspaceSlug = rootStore.router.workspaceSlug; + if (this.teamspaceId !== rootStore.router.teamspaceId) this.teamspaceId = rootStore.router.teamspaceId; + if (this.projectId !== rootStore.router.projectId) this.projectId = rootStore.router.projectId; + if (this.cycleId !== rootStore.router.cycleId) this.cycleId = rootStore.router.cycleId; + if (this.moduleId !== rootStore.router.moduleId) this.moduleId = rootStore.router.moduleId; + if (this.viewId !== rootStore.router.viewId) this.viewId = rootStore.router.viewId; + if (this.globalViewId !== rootStore.router.globalViewId) this.globalViewId = rootStore.router.globalViewId; + if (this.userId !== rootStore.router.userId) this.userId = rootStore.router.userId; + if (!isEmpty(rootStore?.state?.stateMap)) this.stateMap = rootStore?.state?.stateMap; + if (!isEmpty(rootStore?.state?.projectStates)) this.stateDetails = rootStore?.state?.projectStates; + if (!isEmpty(rootStore?.state?.workspaceStates)) this.workspaceStateDetails = rootStore?.state?.workspaceStates; + if (!isEmpty(rootStore?.label?.labelMap)) this.labelMap = rootStore?.label?.labelMap; + if (!isEmpty(rootStore?.memberRoot?.workspace?.workspaceMemberMap)) + this.workSpaceMemberRolesMap = rootStore?.memberRoot?.workspace?.memberMap || undefined; + if (!isEmpty(rootStore?.memberRoot?.memberMap)) this.memberMap = rootStore?.memberRoot?.memberMap || undefined; + if (!isEmpty(rootStore?.projectRoot?.project?.projectMap)) + this.projectMap = rootStore?.projectRoot?.project?.projectMap; + if (!isEmpty(rootStore?.module?.moduleMap)) this.moduleMap = rootStore?.module?.moduleMap; + if (!isEmpty(rootStore?.cycle?.cycleMap)) this.cycleMap = rootStore?.cycle?.cycleMap; + }); }); this.issues = new IssueStore(); diff --git a/apps/web/core/store/workspace/index.ts b/apps/web/core/store/workspace/index.ts index 27bb4701e..cd7f1cf49 100644 --- a/apps/web/core/store/workspace/index.ts +++ b/apps/web/core/store/workspace/index.ts @@ -190,7 +190,9 @@ export class BaseWorkspaceRootStore implements IWorkspaceRootStore { }); return workspaceResponse; } finally { - this.loader = false; + runInAction(() => { + this.loader = false; + }); } }; diff --git a/packages/shared-state/src/store/work-item-filters/filter.store.ts b/packages/shared-state/src/store/work-item-filters/filter.store.ts index f7826ccc9..be78621d2 100644 --- a/packages/shared-state/src/store/work-item-filters/filter.store.ts +++ b/packages/shared-state/src/store/work-item-filters/filter.store.ts @@ -4,7 +4,7 @@ * See the LICENSE file for details. */ -import { action, makeObservable, observable } from "mobx"; +import { action, makeObservable, observable, runInAction } from "mobx"; import { computedFn } from "mobx-utils"; // plane imports import type { TExpressionOptions } from "@plane/constants"; @@ -49,6 +49,7 @@ export interface IWorkItemFilterStore { export class WorkItemFilterStore implements IWorkItemFilterStore { // observable filters: IWorkItemFilterStore["filters"]; + private _pendingFilters = new Map(); constructor() { this.filters = new Map(); @@ -69,9 +70,10 @@ export class WorkItemFilterStore implements IWorkItemFilterStore { * @param entityId - The entity id. * @returns The filter instance. */ - getFilter: IWorkItemFilterStore["getFilter"] = computedFn((entityType, entityId) => - this.filters.get(this._getFilterKey(entityType, entityId)) - ); + getFilter: IWorkItemFilterStore["getFilter"] = computedFn((entityType, entityId) => { + const key = this._getFilterKey(entityType, entityId); + return this.filters.get(key) ?? this._pendingFilters.get(key); + }); // ------------ actions ------------ @@ -80,7 +82,8 @@ export class WorkItemFilterStore implements IWorkItemFilterStore { * If the instance already exists, updates its expression options to ensure they're current. */ getOrCreateFilter: IWorkItemFilterStore["getOrCreateFilter"] = action((params) => { - const existingFilter = this.getFilter(params.entityType, params.entityId); + const filterKey = this._getFilterKey(params.entityType, params.entityId); + const existingFilter = this._pendingFilters.get(filterKey) ?? this.getFilter(params.entityType, params.entityId); if (existingFilter) { // Update expression options on existing filter to ensure they're current if (params.expressionOptions) { @@ -99,8 +102,13 @@ export class WorkItemFilterStore implements IWorkItemFilterStore { // create new filter instance const newFilter = this._initializeFilterInstance(params); - const filterKey = this._getFilterKey(params.entityType, params.entityId); - this.filters.set(filterKey, newFilter); + this._pendingFilters.set(filterKey, newFilter); + queueMicrotask(() => { + runInAction(() => { + this._pendingFilters.delete(filterKey); + this.filters.set(filterKey, newFilter); + }); + }); return newFilter; }); @@ -193,7 +201,9 @@ export class WorkItemFilterStore implements IWorkItemFilterStore { * @param entityId - The entity id. */ deleteFilter: IWorkItemFilterStore["deleteFilter"] = action((entityType, entityId) => { - this.filters.delete(this._getFilterKey(entityType, entityId)); + const key = this._getFilterKey(entityType, entityId); + this._pendingFilters.delete(key); + this.filters.delete(key); }); // ------------ private helpers ------------ diff --git a/packages/ui/src/drag-handle.tsx b/packages/ui/src/drag-handle.tsx index dc67d087b..175e8c700 100644 --- a/packages/ui/src/drag-handle.tsx +++ b/packages/ui/src/drag-handle.tsx @@ -12,23 +12,24 @@ import { cn } from "./utils"; interface IDragHandle { className?: string; disabled?: boolean; + as?: "button" | "div"; } export const DragHandle = forwardRef(function DragHandle( props: IDragHandle, - ref: React.ForwardedRef + ref: React.ForwardedRef ) { - const { className, disabled = false } = props; + const { className, disabled = false, as: Component = "button" } = props; if (disabled) { return
; } return ( - + ); }); diff --git a/packages/ui/src/dropdowns/custom-menu.tsx b/packages/ui/src/dropdowns/custom-menu.tsx index 7b202a3c7..5baeb907c 100644 --- a/packages/ui/src/dropdowns/custom-menu.tsx +++ b/packages/ui/src/dropdowns/custom-menu.tsx @@ -246,18 +246,17 @@ function CustomMenu(props: ICustomMenuDropdownProps) { {({ open }) => ( <> {customButton ? ( - - + // oxlint-disable-next-line jsx_a11y/click-events-have-key-events oxlint-disable-next-line jsx_a11y/no-static-element-interactions + + {customButton} ) : ( <> @@ -462,32 +461,33 @@ function SubMenu(props: ICustomSubMenuProps) { } function MenuItem(props: ICustomMenuItemProps) { - const { children, disabled = false, onClick, className } = props; + const { children, disabled = false, onClick, className, as: Component = "button" } = props; const submenuContext = useSubMenu(); return ( {({ active, close }) => ( - + )} ); diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index 78eed100b..4dabb4a1b 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -99,22 +99,20 @@ export function CustomSearchSelect(props: ICustomSearchSelectProps) { return ( <> {customButton ? ( - - + + {customButton} ) : ( diff --git a/packages/ui/src/dropdowns/custom-select.tsx b/packages/ui/src/dropdowns/custom-select.tsx index ef0724d67..ffd8f77dc 100644 --- a/packages/ui/src/dropdowns/custom-select.tsx +++ b/packages/ui/src/dropdowns/custom-select.tsx @@ -82,17 +82,15 @@ function CustomSelect(props: ICustomSelectProps) { > <> {customButton ? ( - - + + {customButton} ) : ( diff --git a/packages/ui/src/dropdowns/helper.tsx b/packages/ui/src/dropdowns/helper.tsx index 508686d1c..8ca48504a 100644 --- a/packages/ui/src/dropdowns/helper.tsx +++ b/packages/ui/src/dropdowns/helper.tsx @@ -94,6 +94,7 @@ export interface ICustomMenuItemProps { disabled?: boolean; onClick?: (args?: any) => void; className?: string; + as?: "button" | "div"; } export interface ICustomSelectItemProps {