[WEB-5762] fix: workitem detail sidebar properties design consistency (#8400)

This commit is contained in:
Jayash Tripathy 2025-12-19 18:15:56 +05:30 committed by GitHub
parent 3876bf054c
commit 67c39dfc3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 101 additions and 62 deletions

View file

@ -19,7 +19,7 @@ export function SidebarPropertyListItem(props: TSidebarPropertyListItemProps) {
<span>{label}</span>
{appendElement}
</div>
<div className={cn("grow flex items-center flex-wrap gap-2", childrenClassName)}>{children}</div>
<div className={cn("grow flex items-center flex-wrap gap-1", childrenClassName)}>{children}</div>
</div>
);
}

View file

@ -1,5 +1,6 @@
import React from "react";
// helpers
import { Button } from "@plane/propel/button";
import { Tooltip } from "@plane/propel/tooltip";
import { cn } from "@plane/utils";
// types
@ -71,9 +72,11 @@ function BorderButton(props: ButtonProps) {
isMobile={isMobile}
renderByDefault={renderToolTipByDefault}
>
<div
<Button
variant="ghost"
size="sm"
className={cn(
"h-full w-full flex items-center gap-1.5 border-[0.5px] border-strong hover:bg-layer-transparent-hover text-11 px-2 py-0.5 rounded-sm",
"h-full w-full flex items-center justify-start gap-1.5 border-[0.5px] border-strong",
{
"bg-layer-transparent-active": isActive,
},
@ -81,7 +84,7 @@ function BorderButton(props: ButtonProps) {
)}
>
{children}
</div>
</Button>
</Tooltip>
);
}
@ -97,14 +100,16 @@ function BackgroundButton(props: ButtonProps) {
isMobile={isMobile}
renderByDefault={renderToolTipByDefault}
>
<div
<Button
variant="ghost"
size="sm"
className={cn(
"h-full w-full flex items-center gap-1.5 rounded-sm text-11 px-2 py-0.5 bg-layer-3 hover:bg-layer-1-hover",
"h-full w-full flex items-center justify-start gap-1.5 bg-layer-3 hover:bg-layer-1-hover",
className
)}
>
{children}
</div>
</Button>
</Tooltip>
);
}
@ -120,9 +125,11 @@ function TransparentButton(props: ButtonProps) {
isMobile={isMobile}
renderByDefault={renderToolTipByDefault}
>
<div
<Button
variant="ghost"
size="sm"
className={cn(
"h-full w-full flex items-center gap-1.5 rounded-sm text-11 px-2 py-0.5 hover:bg-layer-transparent-hover",
"h-full w-full flex items-center justify-start gap-1.5",
{
"bg-layer-transparent-active": isActive,
},
@ -130,7 +137,7 @@ function TransparentButton(props: ButtonProps) {
)}
>
{children}
</div>
</Button>
</Tooltip>
);
}

View file

@ -138,7 +138,9 @@ export const DateDropdown = observer(function DateDropdown(props: Props) {
>
{!hideIcon && icon}
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
<span className="flex-grow truncate">{value ? renderFormattedDate(value, formatToken) : placeholder}</span>
<span className="flex-grow truncate text-left text-body-xs-medium">
{value ? renderFormattedDate(value, formatToken) : placeholder}
</span>
)}
{isClearable && !disabled && isDateSelected && (
<CloseIcon

View file

@ -144,7 +144,7 @@ export const MemberDropdownBase = observer(function MemberDropdownBase(props: TM
>
{!hideIcon && <ButtonAvatars showTooltip={showTooltip} userIds={value} icon={icon} />}
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
<span className="flex-grow truncate leading-5">
<span className="flex-grow truncate leading-5 text-left text-body-xs-medium">
{getDisplayName(value, showUserDetails, placeholder)}
</span>
)}

View file

@ -120,7 +120,14 @@ function BorderButton(props: ButtonProps) {
<SignalHigh className="size-3" />
))}
{!hideText && (
<span className="flex-grow truncate text-caption-sm-regular">{priorityDetails?.title ?? placeholder}</span>
<span
className={cn("flex-grow truncate text-body-xs-medium", {
"text-secondary": priority && priority !== "none",
"text-placeholder": !priority || priority === "none",
})}
>
{priorityDetails?.title ?? placeholder}
</span>
)}
{dropdownArrow && (
<ChevronDownIcon className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
@ -204,7 +211,12 @@ function BackgroundButton(props: ButtonProps) {
<SignalHigh className="size-3" />
))}
{!hideText && (
<span className="flex-grow truncate text-caption-sm-regular">
<span
className={cn("flex-grow truncate text-body-xs-medium", {
"text-secondary": priority && priority !== "none",
"text-placeholder": !priority || priority === "none",
})}
>
{priorityDetails?.title ?? t("common.priority") ?? placeholder}
</span>
)}
@ -246,7 +258,7 @@ function TransparentButton(props: ButtonProps) {
>
<div
className={cn(
"h-full w-full flex items-center gap-1.5 rounded-sm px-2 py-0.5 hover:bg-layer-transparent-hover",
"h-full w-full flex items-center gap-1.5 rounded-sm hover:bg-layer-transparent-hover px-2",
{
// compact the icons if text is hidden
"px-0.5": hideText,
@ -283,7 +295,12 @@ function TransparentButton(props: ButtonProps) {
<SignalHigh className="size-3" />
))}
{!hideText && (
<span className="flex-grow truncate text-caption-sm-regular">
<span
className={cn("flex-grow truncate text-body-xs-medium", {
"text-secondary": priority && priority !== "none",
"text-placeholder": !priority || priority === "none",
})}
>
{priorityDetails?.title ?? t("common.priority") ?? placeholder}
</span>
)}

View file

@ -50,8 +50,8 @@ export const IssueCycleSelect = observer(function IssueCycleSelect(props: TIssue
disabled={disableSelect}
buttonVariant="transparent-with-text"
className="group w-full"
buttonContainerClassName="w-full text-left rounded-sm"
buttonClassName={`text-13 justify-between ${issue?.cycle_id ? "" : "text-placeholder"}`}
buttonContainerClassName="w-full text-left h-7.5 rounded-sm"
buttonClassName={`text-body-xs-medium justify-between ${issue?.cycle_id ? "" : "text-placeholder"}`}
placeholder={t("cycle.no_cycle")}
hideIcon
dropdownArrow

View file

@ -1,6 +1,6 @@
import { observer } from "mobx-react";
import { Button } from "@plane/propel/button";
import { CloseIcon, LabelFilledIcon } from "@plane/propel/icons";
import { cn } from "@plane/utils";
// types
import { useLabel } from "@/hooks/store/use-label";
import type { TLabelOperations } from "./root";
@ -31,25 +31,14 @@ export const LabelListItem = observer(function LabelListItem(props: TLabelListIt
if (!label) return <></>;
return (
<button
key={labelId}
type="button"
className={cn(
"h-full w-min flex items-center gap-1.5 rounded-sm px-2 py-0.5 bg-layer-transparent-active group text-body-xs-regular text-tertiary",
{
"cursor-pointer": !disabled,
}
)}
onClick={handleLabel}
disabled={disabled}
>
<Button key={labelId} type="button" variant="tertiary" onClick={() => void handleLabel()} disabled={disabled}>
<LabelFilledIcon className="size-3" color={label.color ?? "#000000"} />
<div className="flex-shrink-0 text-body-xs-regular">{label.name}</div>
<div className="flex-shrink-0">{label.name}</div>
{!disabled && (
<div className="flex-shrink-0">
<CloseIcon className="transition-all h-2.5 w-2.5 group-hover:text-danger" />
</div>
)}
</button>
</Button>
);
});

View file

@ -92,7 +92,7 @@ export const IssueLabel = observer(function IssueLabel(props: TIssueLabel) {
);
return (
<div className="relative flex flex-wrap items-center gap-1 min-h-7.5 w-full">
<div className="relative flex flex-wrap items-center gap-1 min-h-7.5 w-full px-2">
<LabelList
workspaceSlug={workspaceSlug}
projectId={projectId}

View file

@ -1,11 +1,12 @@
import { Fragment, useState } from "react";
import { observer } from "mobx-react";
import { usePopper } from "react-popper";
import { Check, Loader, Search } from "lucide-react";
import { Check, Loader, Plus, Search } from "lucide-react";
import { Combobox } from "@headlessui/react";
// plane imports
import { EUserPermissionsLevel, getRandomLabelColor } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import type { IIssueLabel } from "@plane/types";
import { EUserProjectRoles } from "@plane/types";
// helpers
@ -84,11 +85,7 @@ export const IssueLabelSelect = observer(function IssueLabelSelect(props: IIssue
const issueLabels = values ?? [];
const label = (
<span className="size-full flex items-center rounded-sm px-2 py-0.5 bg-layer-transparent hover:bg-layer-transparent-hover text-body-xs-regular text-tertiary">
{t("label.select")}
</span>
);
const label = <span className="text-body-xs-medium text-placeholder">{t("label.select")}</span>;
const searchInputKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
if (query !== "" && e.key === "Escape") {
@ -123,14 +120,16 @@ export const IssueLabelSelect = observer(function IssueLabelSelect(props: IIssue
multiple
>
<Combobox.Button as={Fragment}>
<button
<Button
ref={setReferenceElement}
type="button"
className="cursor-pointer size-full"
variant="tertiary"
size="sm"
prependIcon={<Plus />}
onClick={() => !projectLabels && fetchLabels()}
>
{label}
</button>
</Button>
</Combobox.Button>
<Combobox.Options className="fixed z-10">

View file

@ -64,8 +64,8 @@ export const IssueModuleSelect = observer(function IssueModuleSelect(props: TIss
placeholder={t("module.no_module")}
disabled={disableSelect}
className="group h-full w-full"
buttonContainerClassName="w-full rounded-sm"
buttonClassName={`min-h-8 text-13 justify-between ${issue?.module_ids?.length ? "" : "text-placeholder"}`}
buttonContainerClassName="w-full text-left h-7.5 rounded-sm"
buttonClassName={`text-body-xs-medium justify-between ${issue?.module_ids?.length ? "" : "text-placeholder"}`}
buttonVariant="transparent-with-text"
hideIcon
dropdownArrow

View file

@ -84,7 +84,7 @@ export const IssueParentSelect = observer(function IssueParentSelect(props: TIss
disabled={disabled}
>
{issue.parent_id && parentIssue ? (
<div className="flex items-center gap-1 bg-success-subtle rounded-sm px-1.5 py-1">
<div className="flex items-center gap-1.5">
<Tooltip tooltipHeading="Title" tooltipContent={parentIssue.name} isMobile={isMobile}>
<Link href={workItemLink} target="_blank" rel="noopener noreferrer" onClick={(e) => e.stopPropagation()}>
{parentIssue?.project_id && parentIssueProjectDetails && (
@ -94,7 +94,7 @@ export const IssueParentSelect = observer(function IssueParentSelect(props: TIss
projectIdentifier={parentIssueProjectDetails?.identifier}
issueSequenceId={parentIssue.sequence_id}
size="xs"
variant="success"
variant="secondary"
/>
)}
</Link>
@ -115,7 +115,7 @@ export const IssueParentSelect = observer(function IssueParentSelect(props: TIss
)}
</div>
) : (
<span className="text-body-xs-regular text-placeholder">{t("issue.add.parent")}</span>
<span className="text-body-xs-medium text-placeholder">{t("issue.add.parent")}</span>
)}
{!disabled && (
<span

View file

@ -75,7 +75,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
return (
<div>
<h6 className="text-body-xs-medium">{t("common.properties")}</h6>
<div className={`w-full space-y-2 mt-3 ${disabled ? "opacity-60" : ""}`}>
<div className={`w-full space-y-3 mt-3 ${disabled ? "opacity-60" : ""}`}>
<SidebarPropertyListItem icon={StatePropertyIcon} label={t("common.state")}>
<StateDropdown
value={issue?.state_id}
@ -85,7 +85,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
buttonVariant="transparent-with-text"
className="w-full grow group"
buttonContainerClassName="w-full text-left h-7.5"
buttonClassName="text-body-xs-regular"
buttonClassName={`text-body-xs-medium ${issue?.state_id ? "" : "text-placeholder"}`}
dropdownArrow
dropdownArrowClassName="h-3.5 w-3.5 hidden group-hover:inline"
/>
@ -102,7 +102,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
buttonVariant={issue?.assignee_ids?.length > 1 ? "transparent-without-text" : "transparent-with-text"}
className="w-full grow group"
buttonContainerClassName="w-full text-left h-7.5"
buttonClassName={`text-body-xs-regular justify-between ${issue?.assignee_ids?.length > 0 ? "" : "text-placeholder"}`}
buttonClassName={`text-body-xs-medium justify-between ${issue?.assignee_ids?.length > 0 ? "" : "text-placeholder"}`}
hideIcon={issue.assignee_ids?.length === 0}
dropdownArrow
dropdownArrowClassName="h-3.5 w-3.5 hidden group-hover:inline"
@ -116,18 +116,22 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
disabled={disabled}
buttonVariant="transparent-with-text"
className="w-full h-7.5 grow rounded-sm"
buttonContainerClassName="size-full text-left"
buttonClassName="size-full px-2 py-0.5 whitespace-nowrap [&_svg]:size-3.5"
buttonContainerClassName="w-full text-left h-7.5"
buttonClassName={`text-body-xs-medium whitespace-nowrap [&_svg]:size-3.5 ${!issue?.priority || issue?.priority === "none" ? "text-placeholder" : ""}`}
/>
</SidebarPropertyListItem>
{createdByDetails && (
<SidebarPropertyListItem icon={UserCirclePropertyIcon} label={t("common.created_by")}>
<SidebarPropertyListItem
icon={UserCirclePropertyIcon}
label={t("common.created_by")}
childrenClassName="px-2"
>
<ButtonAvatars
showTooltip
userIds={createdByDetails?.display_name.includes("-intake") ? null : createdByDetails?.id}
/>
<span className="grow truncate leading-5">
<span className="grow truncate text-body-xs-medium text-secondary leading-5">
{createdByDetails?.display_name.includes("-intake") ? "Plane" : createdByDetails?.display_name}
</span>
</SidebarPropertyListItem>
@ -147,7 +151,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
disabled={disabled}
className="w-full grow group"
buttonContainerClassName="w-full text-left h-7.5"
buttonClassName={`text-body-xs-regular ${issue?.start_date ? "" : "text-placeholder"}`}
buttonClassName={`text-body-xs-medium ${issue?.start_date ? "" : "text-placeholder"}`}
hideIcon
clearIconClassName="h-3 w-3 hidden group-hover:inline"
/>
@ -168,7 +172,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
disabled={disabled}
className="w-full grow group"
buttonContainerClassName="w-full text-left h-7.5"
buttonClassName={cn("text-body-xs-regular", {
buttonClassName={cn("text-body-xs-medium", {
"text-placeholder": !issue.target_date,
"text-danger": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
})}
@ -189,7 +193,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
buttonVariant="transparent-with-text"
className="w-full grow group"
buttonContainerClassName="w-full text-left h-7.5"
buttonClassName={`text-body-xs-regular ${issue?.estimate_point !== undefined ? "" : "text-placeholder"}`}
buttonClassName={`text-body-xs-medium ${issue?.estimate_point !== undefined ? "" : "text-placeholder"}`}
placeholder="None"
hideIcon
dropdownArrow

View file

@ -34,6 +34,28 @@ export const FiltersToggle = observer(function FiltersToggle<P extends TFilterPr
filter.toggleVisibility();
};
// Base classes when filter is active
const activeFilterBaseClasses =
"text-accent-primary border border-accent-subtle-1 hover:border-accent-subtle-1 active:border-accent-subtle-1 focus:border-accent-subtle-1";
// State classes that prevent hover/active/focus color changes
const noHoverStateClasses = "hover:text-accent-primary active:text-accent-primary focus:text-accent-primary";
// Background classes based on toggle state (darker when open, lighter when closed)
const backgroundClasses = isFilterRowVisible
? "bg-accent-subtle-hover hover:bg-accent-subtle-hover active:bg-accent-subtle-hover focus:bg-accent-subtle-hover"
: "bg-accent-subtle hover:bg-accent-subtle active:bg-accent-subtle focus:bg-accent-subtle";
const buttonClassName = cn({
[activeFilterBaseClasses]: showFilterRowChangesPill,
[backgroundClasses]: showFilterRowChangesPill,
[noHoverStateClasses]: showFilterRowChangesPill,
});
const iconClassName = cn({
"text-accent-primary [&_path]:fill-current": showFilterRowChangesPill,
});
// Show the add filter button when there are no active conditions, the filter row is hidden, and no unsaved changes exist
if (filter && showAddFilterButton) {
return (
@ -55,9 +77,8 @@ export const FiltersToggle = observer(function FiltersToggle<P extends TFilterPr
variant="secondary"
icon={showFilterRowChangesPill ? FilterAppliedIcon : FilterIcon}
onClick={handleToggleFilter}
className={cn({
"text-accent-primary bg-accent-subtle border border-accent-subtle-1": showFilterRowChangesPill,
})}
className={buttonClassName}
iconClassName={iconClassName}
/>
);
});

View file

@ -295,7 +295,7 @@
/* Background colors */
--background-color-canvas: var(--color-neutral-300);
--background-color-surface-1: var(--color-neutral-white);
--background-color-surface-2: var(--color-neutral-100);
--background-color-surface-2: var(--color-neutral-200);
--background-color-layer-1: var(--color-neutral-200);
--background-color-layer-1-hover: var(--color-neutral-300);
--background-color-layer-1-active: var(--color-neutral-400);