fix: existing and parent issue modal empty state flicker (#4281)

This commit is contained in:
Anmol Singh Bhatia 2024-04-24 20:48:44 +05:30 committed by GitHub
parent e60ef36bfe
commit 15c7deb2db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 112 additions and 95 deletions

View File

@ -36,6 +36,7 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
workspaceLevelToggle = false, workspaceLevelToggle = false,
} = props; } = props;
// states // states
const [isLoading, setIsLoading] = useState(false);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [issues, setIssues] = useState<ISearchIssueResponse[]>([]); const [issues, setIssues] = useState<ISearchIssueResponse[]>([]);
const [selectedIssues, setSelectedIssues] = useState<ISearchIssueResponse[]>([]); const [selectedIssues, setSelectedIssues] = useState<ISearchIssueResponse[]>([]);
@ -72,7 +73,7 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
useEffect(() => { useEffect(() => {
if (!isOpen || !workspaceSlug || !projectId) return; if (!isOpen || !workspaceSlug || !projectId) return;
setIsLoading(true);
projectService projectService
.projectIssuesSearch(workspaceSlug as string, projectId as string, { .projectIssuesSearch(workspaceSlug as string, projectId as string, {
search: debouncedSearchTerm, search: debouncedSearchTerm,
@ -80,7 +81,10 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
workspace_search: isWorkspaceLevel, workspace_search: isWorkspaceLevel,
}) })
.then((res) => setIssues(res)) .then((res) => setIssues(res))
.finally(() => setIsSearching(false)); .finally(() => {
setIsSearching(false);
setIsLoading(false);
});
}, [debouncedSearchTerm, isOpen, isWorkspaceLevel, projectId, searchParams, workspaceSlug]); }, [debouncedSearchTerm, isOpen, isWorkspaceLevel, projectId, searchParams, workspaceSlug]);
return ( return (
@ -194,14 +198,7 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
</h5> </h5>
)} )}
<IssueSearchModalEmptyState {isSearching || isLoading ? (
debouncedSearchTerm={debouncedSearchTerm}
isSearching={isSearching}
issues={issues}
searchTerm={searchTerm}
/>
{isSearching ? (
<Loader className="space-y-3 p-3"> <Loader className="space-y-3 p-3">
<Loader.Item height="40px" /> <Loader.Item height="40px" />
<Loader.Item height="40px" /> <Loader.Item height="40px" />
@ -209,48 +206,59 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
<Loader.Item height="40px" /> <Loader.Item height="40px" />
</Loader> </Loader>
) : ( ) : (
<ul className={`text-sm text-custom-text-100 ${issues.length > 0 ? "p-2" : ""}`}> <>
{issues.map((issue) => { {issues.length === 0 ? (
const selected = selectedIssues.some((i) => i.id === issue.id); <IssueSearchModalEmptyState
debouncedSearchTerm={debouncedSearchTerm}
isSearching={isSearching}
issues={issues}
searchTerm={searchTerm}
/>
) : (
<ul className={`text-sm text-custom-text-100 ${issues.length > 0 ? "p-2" : ""}`}>
{issues.map((issue) => {
const selected = selectedIssues.some((i) => i.id === issue.id);
return ( return (
<Combobox.Option <Combobox.Option
key={issue.id} key={issue.id}
as="label" as="label"
htmlFor={`issue-${issue.id}`} htmlFor={`issue-${issue.id}`}
value={issue} value={issue}
className={({ active }) => className={({ active }) =>
`group flex w-full cursor-pointer select-none items-center justify-between gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ `group flex w-full cursor-pointer select-none items-center justify-between gap-2 rounded-md px-3 py-2 text-custom-text-200 ${
active ? "bg-custom-background-80 text-custom-text-100" : "" active ? "bg-custom-background-80 text-custom-text-100" : ""
} ${selected ? "text-custom-text-100" : ""}` } ${selected ? "text-custom-text-100" : ""}`
} }
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<input type="checkbox" checked={selected} readOnly /> <input type="checkbox" checked={selected} readOnly />
<span <span
className="block h-1.5 w-1.5 flex-shrink-0 rounded-full" className="block h-1.5 w-1.5 flex-shrink-0 rounded-full"
style={{ style={{
backgroundColor: issue.state__color, backgroundColor: issue.state__color,
}} }}
/> />
<span className="flex-shrink-0 text-xs"> <span className="flex-shrink-0 text-xs">
{issue.project__identifier}-{issue.sequence_id} {issue.project__identifier}-{issue.sequence_id}
</span> </span>
{issue.name} {issue.name}
</div> </div>
<a <a
href={`/${workspaceSlug}/projects/${issue.project_id}/issues/${issue.id}`} href={`/${workspaceSlug}/projects/${issue.project_id}/issues/${issue.id}`}
target="_blank" target="_blank"
className="z-1 relative hidden text-custom-text-200 hover:text-custom-text-100 group-hover:block" className="z-1 relative hidden text-custom-text-200 hover:text-custom-text-100 group-hover:block"
rel="noopener noreferrer" rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<Rocket className="h-4 w-4" /> <Rocket className="h-4 w-4" />
</a> </a>
</Combobox.Option> </Combobox.Option>
); );
})} })}
</ul> </ul>
)}
</>
)} )}
</Combobox.Options> </Combobox.Options>
</Combobox> </Combobox>

View File

@ -36,6 +36,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
projectId, projectId,
issueId, issueId,
}) => { }) => {
const [isLoading, setIsLoading] = useState(false);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [issues, setIssues] = useState<ISearchIssueResponse[]>([]); const [issues, setIssues] = useState<ISearchIssueResponse[]>([]);
const [isSearching, setIsSearching] = useState(false); const [isSearching, setIsSearching] = useState(false);
@ -56,6 +57,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
if (!isOpen || !workspaceSlug || !projectId) return; if (!isOpen || !workspaceSlug || !projectId) return;
setIsSearching(true); setIsSearching(true);
setIsLoading(true);
projectService projectService
.projectIssuesSearch(workspaceSlug as string, projectId as string, { .projectIssuesSearch(workspaceSlug as string, projectId as string, {
@ -65,7 +67,10 @@ export const ParentIssuesListModal: React.FC<Props> = ({
workspace_search: isWorkspaceLevel, workspace_search: isWorkspaceLevel,
}) })
.then((res) => setIssues(res)) .then((res) => setIssues(res))
.finally(() => setIsSearching(false)); .finally(() => {
setIsSearching(false);
setIsLoading(false);
});
}, [debouncedSearchTerm, isOpen, issueId, isWorkspaceLevel, projectId, workspaceSlug]); }, [debouncedSearchTerm, isOpen, issueId, isWorkspaceLevel, projectId, workspaceSlug]);
return ( return (
@ -153,14 +158,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
</h5> </h5>
)} )}
<IssueSearchModalEmptyState {isSearching || isLoading ? (
debouncedSearchTerm={debouncedSearchTerm}
isSearching={isSearching}
issues={issues}
searchTerm={searchTerm}
/>
{isSearching ? (
<Loader className="space-y-3 p-3"> <Loader className="space-y-3 p-3">
<Loader.Item height="40px" /> <Loader.Item height="40px" />
<Loader.Item height="40px" /> <Loader.Item height="40px" />
@ -168,41 +166,52 @@ export const ParentIssuesListModal: React.FC<Props> = ({
<Loader.Item height="40px" /> <Loader.Item height="40px" />
</Loader> </Loader>
) : ( ) : (
<ul className={`text-sm ${issues.length > 0 ? "p-2" : ""}`}> <>
{issues.map((issue) => ( {issues.length === 0 ? (
<Combobox.Option <IssueSearchModalEmptyState
key={issue.id} debouncedSearchTerm={debouncedSearchTerm}
value={issue} isSearching={isSearching}
className={({ active, selected }) => issues={issues}
`group flex w-full cursor-pointer select-none items-center justify-between gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ searchTerm={searchTerm}
active ? "bg-custom-background-80 text-custom-text-100" : "" />
} ${selected ? "text-custom-text-100" : ""}` ) : (
} <ul className={`text-sm ${issues.length > 0 ? "p-2" : ""}`}>
> {issues.map((issue) => (
<div className="flex flex-grow items-center gap-2 truncate"> <Combobox.Option
<span key={issue.id}
className="block h-1.5 w-1.5 flex-shrink-0 rounded-full" value={issue}
style={{ className={({ active, selected }) =>
backgroundColor: issue.state__color, `group flex w-full cursor-pointer select-none items-center justify-between gap-2 rounded-md px-3 py-2 text-custom-text-200 ${
}} active ? "bg-custom-background-80 text-custom-text-100" : ""
/> } ${selected ? "text-custom-text-100" : ""}`
<span className="flex-shrink-0 text-xs"> }
{issue.project__identifier}-{issue.sequence_id} >
</span>{" "} <div className="flex flex-grow items-center gap-2 truncate">
<span className="truncate">{issue.name}</span> <span
</div> className="block h-1.5 w-1.5 flex-shrink-0 rounded-full"
<a style={{
href={`/${workspaceSlug}/projects/${issue.project_id}/issues/${issue.id}`} backgroundColor: issue.state__color,
target="_blank" }}
className="z-1 relative hidden flex-shrink-0 text-custom-text-200 hover:text-custom-text-100 group-hover:block" />
rel="noopener noreferrer" <span className="flex-shrink-0 text-xs">
onClick={(e) => e.stopPropagation()} {issue.project__identifier}-{issue.sequence_id}
> </span>{" "}
<Rocket className="h-4 w-4" /> <span className="truncate">{issue.name}</span>
</a> </div>
</Combobox.Option> <a
))} href={`/${workspaceSlug}/projects/${issue.project_id}/issues/${issue.id}`}
</ul> target="_blank"
className="z-1 relative hidden flex-shrink-0 text-custom-text-200 hover:text-custom-text-100 group-hover:block"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
>
<Rocket className="h-4 w-4" />
</a>
</Combobox.Option>
))}
</ul>
)}
</>
)} )}
</Combobox.Options> </Combobox.Options>
</Combobox> </Combobox>