feat: issues tooltip , fix: ui improvement (#317)

* fix: ellipsis added to issue title

* feat: toolttip added

* feat: assignees tooltip added

* fix: build fix
This commit is contained in:
Anmol Singh Bhatia 2023-02-22 11:42:17 +05:30 committed by GitHub
parent 2cadb3784b
commit d8c10b6bc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 125 additions and 108 deletions

View File

@ -16,8 +16,10 @@ import {
ViewPrioritySelect,
ViewStateSelect,
} from "components/issues/view-select";
import { Tooltip2 } from "@blueprintjs/popover2";
// ui
import { CustomMenu } from "components/ui";
import { Tooltip, CustomMenu } from "components/ui";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
@ -151,11 +153,20 @@ export const SingleListIssue: React.FC<Props> = ({
<Link href={`/${workspaceSlug}/projects/${issue?.project_detail?.id}/issues/${issue.id}`}>
<a className="group relative flex items-center gap-2">
{properties.key && (
<span className="flex-shrink-0 text-xs text-gray-500">
{issue.project_detail?.identifier}-{issue.sequence_id}
</span>
<Tooltip
tooltipHeading="ID"
tooltipContent={`${issue.project_detail?.identifier}-${issue.sequence_id}`}
>
<span className="flex-shrink-0 text-xs text-gray-500">
{issue.project_detail?.identifier}-{issue.sequence_id}
</span>
</Tooltip>
)}
<span>{issue.name}</span>
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
<span className="w-auto max-w-lg text-ellipsis overflow-hidden whitespace-nowrap">
{issue.name}
</span>
</Tooltip>
</a>
</Link>
</div>

View File

@ -82,7 +82,9 @@ export const MyIssuesListItem: React.FC<Props> = ({
{issue.project_detail?.identifier}-{issue.sequence_id}
</span>
)}
<span>{issue.name}</span>
<span className="w-[275px] md:w-[450px] lg:w-[600px] text-ellipsis overflow-hidden whitespace-nowrap">
{issue.name}
</span>
</a>
</Link>
</div>

View File

@ -9,7 +9,7 @@ import { Listbox, Transition } from "@headlessui/react";
// services
import projectService from "services/project.service";
// ui
import { AssigneesList, Avatar } from "components/ui";
import { AssigneesList, Avatar, Tooltip } from "components/ui";
// types
import { IIssue } from "types";
// fetch-keys
@ -56,13 +56,26 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
{({ open }) => (
<div>
<Listbox.Button>
<div
className={`flex ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
} items-center gap-1 text-xs`}
<Tooltip
tooltipHeading="Assignee"
tooltipContent={
issue.assignee_details.length > 0
? issue.assignee_details
.map((assingee) =>
assingee.first_name !== "" ? assingee.first_name : assingee.email
)
.toString()
: "No Assignee"
}
>
<AssigneesList userIds={issue.assignees ?? []} />
</div>
<div
className={`flex ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
} items-center gap-1 text-xs`}
>
<AssigneesList userIds={issue.assignees ?? []} />
</div>
</Tooltip>
</Listbox.Button>
<Transition

View File

@ -1,5 +1,5 @@
// ui
import { CustomDatePicker } from "components/ui";
import { CustomDatePicker, Tooltip } from "components/ui";
// helpers
import { findHowManyDaysLeft } from "helpers/date-time.helper";
// types
@ -12,25 +12,27 @@ type Props = {
};
export const ViewDueDateSelect: React.FC<Props> = ({ issue, partialUpdateIssue, isNotAllowed }) => (
<div
className={`group relative ${
issue.target_date === null
? ""
: issue.target_date < new Date().toISOString()
? "text-red-600"
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
}`}
>
<CustomDatePicker
placeholder="N/A"
value={issue?.target_date}
onChange={(val) =>
partialUpdateIssue({
target_date: val,
})
}
className={issue?.target_date ? "w-[6.5rem]" : "w-[3rem] text-center"}
disabled={isNotAllowed}
/>
</div>
<Tooltip tooltipHeading="Due Date" tooltipContent={issue.target_date ?? "N/A"}>
<div
className={`group relative ${
issue.target_date === null
? ""
: issue.target_date < new Date().toISOString()
? "text-red-600"
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
}`}
>
<CustomDatePicker
placeholder="N/A"
value={issue?.target_date}
onChange={(val) =>
partialUpdateIssue({
target_date: val,
})
}
className={issue?.target_date ? "w-[6.5rem]" : "w-[3rem] text-center"}
disabled={isNotAllowed}
/>
</div>
</Tooltip>
);

View File

@ -1,7 +1,7 @@
import React from "react";
// ui
import { CustomSelect } from "components/ui";
import { CustomSelect, Tooltip } from "components/ui";
// icons
import { getPriorityIcon } from "components/icons/priority-icon";
// types
@ -24,12 +24,14 @@ export const ViewPrioritySelect: React.FC<Props> = ({
}) => (
<CustomSelect
label={
<span>
{getPriorityIcon(
issue.priority && issue.priority !== "" ? issue.priority ?? "" : "None",
"text-sm"
)}
</span>
<Tooltip tooltipHeading="Priority" tooltipContent={issue.priority ?? "None"}>
<span>
{getPriorityIcon(
issue.priority && issue.priority !== "" ? issue.priority ?? "" : "None",
"text-sm"
)}
</span>
</Tooltip>
}
value={issue.state}
onChange={(data: string) => {

View File

@ -5,7 +5,7 @@ import useSWR from "swr";
// services
import stateService from "services/state.service";
// ui
import { CustomSelect } from "components/ui";
import { CustomSelect, Tooltip } from "components/ui";
// helpers
import { addSpaceIfCamelCase } from "helpers/string.helper";
import { getStatesList } from "helpers/state.helper";
@ -48,7 +48,16 @@ export const ViewStateSelect: React.FC<Props> = ({
backgroundColor: states?.find((s) => s.id === issue.state)?.color,
}}
/>
{addSpaceIfCamelCase(states?.find((s) => s.id === issue.state)?.name ?? "")}
<Tooltip
tooltipHeading="State"
tooltipContent={addSpaceIfCamelCase(
states?.find((s) => s.id === issue.state)?.name ?? ""
)}
>
<span>
{addSpaceIfCamelCase(states?.find((s) => s.id === issue.state)?.name ?? "")}
</span>
</Tooltip>
</>
}
value={issue.state}

View File

@ -184,16 +184,18 @@ export const SingleState: React.FC<Props> = ({
Set as default
</button>
)}
<Tooltip content="Cannot delete the default state." disabled={!state.default}>
<button
type="button"
className={`${state.default ? "cursor-not-allowed" : ""} grid place-items-center`}
onClick={handleDeleteState}
disabled={state.default}
>
<button
type="button"
className={`${state.default ? "cursor-not-allowed" : ""} grid place-items-center`}
onClick={handleDeleteState}
disabled={state.default}
>
<Tooltip tooltipContent="Cannot delete the default state." disabled={!state.default}>
<TrashIcon className="h-4 w-4 text-red-400" />
</button>
</Tooltip>
</Tooltip>
</button>
<button type="button" className="grid place-items-center" onClick={handleEditState}>
<PencilSquareIcon className="h-4 w-4 text-gray-400" />
</button>

View File

@ -18,7 +18,7 @@ type AvatarProps = {
};
export const Avatar: React.FC<AvatarProps> = ({ user, index }) => (
<div className={`relative z-[1] h-5 w-5 rounded-full ${index && index !== 0 ? "-ml-2.5" : ""}`}>
<div className={`relative h-5 w-5 rounded-full ${index && index !== 0 ? "-ml-2.5" : ""}`}>
{user && user.avatar && user.avatar !== "" ? (
<div
className={`h-5 w-5 rounded-full border-2 ${

View File

@ -1,66 +1,40 @@
import React, { useEffect, useState } from "react";
import React from "react";
import { Tooltip2 } from "@blueprintjs/popover2";
export type Props = {
direction?: "top" | "right" | "bottom" | "left";
content: string | React.ReactNode;
margin?: string;
children: React.ReactNode;
className?: string;
tooltipHeading?: string;
tooltipContent: string;
position?: "top" | "right" | "bottom" | "left";
children: JSX.Element;
disabled?: boolean;
};
export const Tooltip: React.FC<Props> = ({
content,
direction = "top",
tooltipHeading,
tooltipContent,
position = "top",
children,
margin = "24px",
className = "",
disabled = false,
}) => {
const [active, setActive] = useState(false);
const [styleConfig, setStyleConfig] = useState(`top-[calc(-100%-${margin})]`);
let timeout: any;
const showToolTip = () => {
timeout = setTimeout(() => {
setActive(true);
}, 300);
};
const hideToolTip = () => {
clearInterval(timeout);
setActive(false);
};
const tooltipStyles = {
top: "left-[50%] translate-x-[-50%] before:contents-[''] before:border-solid before:border-transparent before:h-0 before:w-0 before:absolute before:pointer-events-none before:border-[6px] before:left-[50%] before:ml-[calc(6px*-1)] before:top-full before:border-t-black",
right: "right-[-100%] top-[50%] translate-x-0 translate-y-[-50%]",
bottom:
"left-[50%] translate-x-[-50%] before:contents-[''] before:border-solid before:border-transparent before:h-0 before:w-0 before:absolute before:pointer-events-none before:border-[6px] before:left-[50%] before:ml-[calc(6px*-1)] before:bottom-full before:border-b-black",
left: "left-[-100%] top-[50%] translate-x-0 translate-y-[-50%]",
};
useEffect(() => {
const styleConfig = `${direction}-[calc(-100%-${margin})]`;
setStyleConfig(styleConfig);
}, [margin, direction]);
return (
<div className="relative inline-block" onMouseEnter={showToolTip} onMouseLeave={hideToolTip}>
{children}
{active && (
<div
className={`${className} ${
disabled ? "hidden" : ""
} absolute p-[6px] text-xs z-20 rounded leading-1 text-white bg-black text-center w-max max-w-[300px]
${tooltipStyles[direction]} ${styleConfig}`}
>
{content}
<Tooltip2
disabled={disabled}
content={
<div className="flex flex-col justify-center items-start gap-1 max-w-[600px] text-xs rounded-md bg-white p-2 shadow-md capitalize text-left">
{tooltipHeading ? (
<>
<h5 className="font-medium">{tooltipHeading}</h5>
<p className="text-gray-700">{tooltipContent}</p>
</>
) : (
<p className="text-gray-700">{tooltipContent}</p>
)}
</div>
)}
</div>
}
position={position}
renderTarget={({ isOpen: isTooltipOpen, ref: eleRefernce, ...tooltipProps }) =>
React.cloneElement(children, { ref: eleRefernce, ...tooltipProps, ...children.props })
}
/>
);
};

View File

@ -9,6 +9,8 @@
"lint": "next lint"
},
"dependencies": {
"@blueprintjs/core": "^4.16.3",
"@blueprintjs/popover2": "^1.13.3",
"@headlessui/react": "^1.7.3",
"@heroicons/react": "^2.0.12",
"@remirror/core": "^2.0.11",
@ -46,8 +48,8 @@
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"autoprefixer": "^10.4.7",
"eslint-config-custom": "*",
"eslint": "^8.31.0",
"eslint-config-custom": "*",
"eslint-config-next": "12.2.2",
"postcss": "^8.4.14",
"tailwindcss": "^3.1.6",