* refactor: migrate web @plane/propel Tooltip to @makeplane/propel Move all 144 Tooltip call sites in apps/web from the in-repo @plane/propel/tooltip to the published @makeplane/propel/components/tooltip. Prop mapping applied on every site: - tooltipContent -> label (string) - tooltipHeading -> folded into label + layout="stacked" - position -> side/align (top/center omitted; it is the published default) - isMobile -> disabled (OR'd with any existing disabled) - renderByDefault -> dropped; the in-repo Tooltip declared but never used it - className -> dropped, or mapped to sideOffset/alignOffset (3 sites) layout is "single" for short bounded copy and "stacked" wherever the label interpolates unbounded data (names, titles, URLs, joined lists) — "single" is whitespace-nowrap, so long labels would overflow. Behavior change: tooltips now open after the published 600ms default instead of the in-repo 200ms. delay is deliberately not set per site. ReactNode content: - Editor toolbars map name + <kbd>shortcut</kbd> onto the published shortcut prop - <br/> and fragment copy folds into one wrapping stacked string - stickies/action-bar rendered a full <StickyNote> hover preview, not a tooltip; it moves to PreviewCard/PreviewCardTrigger/PreviewCardContent Published label is a string, so DropdownButtonProps.tooltipContent, date-range's customTooltipContent, and the two rich-filters components narrow from ReactNode to string. rich-filters/root.tsx guards filterConfig.tooltipContent, which stays ReactNode in @plane/types. Folded labels use ?? "" where the source expression is nullable so they cannot render the string "undefined" where the in-repo tooltip rendered nothing. Zero @plane/propel/tooltip references remain under apps/web. * fix: address review findings on the Tooltip migration Accessibility (CodeRabbit): Base UI wires no aria-describedby or aria-labelledby between the tooltip trigger and its popup — verified by rendering the published Tooltip and inspecting the DOM, the trigger comes out as `<button data-base-ui-tooltip-trigger>` with no aria at all and the popup has role="tooltip" but no id. So a `label` never reaches assistive tech, and the four icon-only triggers announced as bare "button". Add an explicit aria-label at each: extended-project-sidebar, layout-switcher, layout-selection, and access-field. Pre-existing gap (the in-repo tooltip sat on the same unwired Base UI trigger), fixed here since the PR touches these sites. AccessField now requires i18n_label and drops the optional pre-i18n `label`, so the computed label is non-empty by construction and serves as both the tooltip copy and the accessible name. Its only caller already passes i18n_label for every specifier, which also retires the TODO. Correctness: inbox issue-properties interpolated a nullable name into a template literal, so a duplicate work item with no name rendered the literal "undefined"; pass the name with ?? "" instead. CodeQL useless conditional: drop `|| ""` from stickyId in action-bar — both call sites already sit behind a guard proving recentStickyId truthy, and StickyNote treats "" and undefined identically. React Doctor prefer-module-scope-static-value: hoist badgeContent to module scope as BADGE_CONTENT; it holds only static strings. React Doctor js-combine-iterations: collapse the filter/map chains to a single pass. The two layout switchers skip inside map instead of pre-filtering; LabelSummary reduces project labels into the selected names once rather than filter -> map -> join.
61 lines
2 KiB
TypeScript
61 lines
2 KiB
TypeScript
/**
|
|
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
* See the LICENSE file for details.
|
|
*/
|
|
|
|
import React from "react";
|
|
// components
|
|
import { Tooltip } from "@makeplane/propel/components/tooltip";
|
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
|
type Props = {
|
|
labelDetails: any[];
|
|
maxRender?: number;
|
|
};
|
|
|
|
export function ViewIssueLabel({ labelDetails, maxRender = 1 }: Props) {
|
|
const { isMobile } = usePlatformOS();
|
|
return (
|
|
<>
|
|
{labelDetails?.length > 0 ? (
|
|
labelDetails.length <= maxRender ? (
|
|
<>
|
|
{labelDetails.map((label) => (
|
|
<div
|
|
key={label.id}
|
|
className="shadow-sm flex flex-shrink-0 cursor-default items-center rounded-md border border-strong px-2.5 py-1 text-11"
|
|
>
|
|
<Tooltip label={`Label: ${label.name}`} layout="stacked" disabled={isMobile}>
|
|
<div className="flex items-center gap-1.5 text-secondary">
|
|
<span
|
|
className="h-2 w-2 flex-shrink-0 rounded-full"
|
|
style={{
|
|
backgroundColor: label?.color ?? "#000000",
|
|
}}
|
|
/>
|
|
{label.name}
|
|
</div>
|
|
</Tooltip>
|
|
</div>
|
|
))}
|
|
</>
|
|
) : (
|
|
<div className="shadow-sm flex flex-shrink-0 cursor-default items-center rounded-md border border-strong px-2.5 py-1 text-11">
|
|
<Tooltip
|
|
label={`Labels: ${labelDetails.map((l) => l.name).join(", ")}`}
|
|
layout="stacked"
|
|
disabled={isMobile}
|
|
>
|
|
<div className="flex items-center gap-1.5 text-secondary">
|
|
<span className="h-2 w-2 flex-shrink-0 rounded-full bg-accent-primary" />
|
|
{`${labelDetails.length} Labels`}
|
|
</div>
|
|
</Tooltip>
|
|
</div>
|
|
)
|
|
) : (
|
|
""
|
|
)}
|
|
</>
|
|
);
|
|
}
|