Skip to content

Commit bd80433

Browse files
mskorokhodovjdolle
andauthored
feat(app): schema explorer field descriptions and more clear enum usage statistics (#7169)
Co-authored-by: jdolle <[email protected]>
1 parent ebd7310 commit bd80433

File tree

5 files changed

+202
-141
lines changed

5 files changed

+202
-141
lines changed

packages/web/app/src/components/target/explorer/common.tsx

Lines changed: 163 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { ReactElement, ReactNode, useMemo } from 'react';
1+
import React, { ReactElement, ReactNode, useLayoutEffect, useMemo, useRef, useState } from 'react';
22
import { clsx } from 'clsx';
33
import { PulseIcon, UsersIcon } from '@/components/ui/icon';
44
import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
@@ -9,6 +9,7 @@ import { FragmentType, graphql, useFragment } from '@/gql';
99
import { SupergraphMetadataList_SupergraphMetadataFragmentFragment } from '@/gql/graphql';
1010
import { formatNumber, toDecimal } from '@/lib/hooks';
1111
import { cn } from '@/lib/utils';
12+
import { capitalize } from '@/utils';
1213
import { ChatBubbleIcon } from '@radix-ui/react-icons';
1314
import { Link as NextLink, useRouter } from '@tanstack/react-router';
1415
import { useArgumentListToggle, useSchemaExplorerContext } from './provider';
@@ -49,6 +50,38 @@ function Description(props: { description: string }) {
4950
);
5051
}
5152

53+
export function DescriptionInline(props: { description: string }) {
54+
const ref = useRef<HTMLDivElement>(null);
55+
const [canExpand, setCanExpand] = useState(false);
56+
const [isExpanded, setIsExpanded] = useState(false);
57+
58+
useLayoutEffect(() => {
59+
if (ref.current) {
60+
setCanExpand(ref.current.scrollHeight > ref.current.clientHeight);
61+
}
62+
}, [props.description]);
63+
64+
return (
65+
<div className="inline-block max-w-screen-sm">
66+
<Markdown
67+
ref={ref}
68+
className={clsx('text-muted-foreground line-clamp-2 text-left text-sm', {
69+
'line-clamp-none': isExpanded,
70+
})}
71+
content={props.description}
72+
/>
73+
{canExpand ? (
74+
<span
75+
className="cursor-pointer text-xs text-orange-500"
76+
onClick={() => setIsExpanded(prev => !prev)}
77+
>
78+
{isExpanded ? 'Show less' : 'Show more'}
79+
</span>
80+
) : null}
81+
</div>
82+
);
83+
}
84+
5285
const SchemaExplorerUsageStats_UsageFragment = graphql(`
5386
fragment SchemaExplorerUsageStats_UsageFragment on SchemaCoordinateUsage {
5487
total
@@ -68,10 +101,13 @@ export function SchemaExplorerUsageStats(props: {
68101
organizationSlug: string;
69102
projectSlug: string;
70103
targetSlug: string;
104+
kindLabel?: string;
71105
}) {
72106
const usage = useFragment(SchemaExplorerUsageStats_UsageFragment, props.usage);
73107
const percentage = props.totalRequests ? (usage.total / props.totalRequests) * 100 : 0;
74108

109+
const kindLabel = useMemo(() => props.kindLabel ?? 'field', [props.kindLabel]);
110+
75111
return (
76112
<TooltipProvider delayDuration={0}>
77113
<div className="ml-3 flex flex-row items-center gap-2 text-xs">
@@ -90,18 +126,19 @@ export function SchemaExplorerUsageStats(props: {
90126
<Tooltip>
91127
<TooltipContent>
92128
<div className="z-10">
93-
<div className="mb-1 text-lg font-bold">Field Usage</div>
129+
<div className="mb-1 text-lg font-bold">{capitalize(kindLabel)} Usage</div>
94130
{usage.isUsed === false ? (
95-
<div>This field is currently not in use.</div>
131+
<div>This {kindLabel} is currently not in use.</div>
96132
) : (
97133
<div>
98134
<ul>
99135
<li>
100-
This field has been queried in <strong>{formatNumber(usage.total)}</strong>{' '}
101-
requests.
136+
This {kindLabel} has been queried in{' '}
137+
<strong>{formatNumber(usage.total)}</strong> requests.
102138
</li>
103139
<li>
104-
<strong>{toDecimal(percentage)}%</strong> of all requests use this field.
140+
<strong>{toDecimal(percentage)}%</strong> of all requests use this {kindLabel}
141+
.
105142
</li>
106143
</ul>
107144

@@ -159,7 +196,7 @@ export function SchemaExplorerUsageStats(props: {
159196

160197
{Array.isArray(usage.usedByClients) && usage.usedByClients.length > 0 ? (
161198
<>
162-
<div className="mb-2">This field is used by the following clients:</div>
199+
<div className="mb-2">This {kindLabel} is used by the following clients:</div>
163200
<ul>
164201
{usage.usedByClients.map(clientName => (
165202
<li key={clientName} className="font-bold">
@@ -180,7 +217,7 @@ export function SchemaExplorerUsageStats(props: {
180217
</ul>
181218
</>
182219
) : (
183-
<div>This field is not used by any client.</div>
220+
<div>This {kindLabel} is not used by any client.</div>
184221
)}
185222
</>
186223
</TooltipContent>
@@ -308,8 +345,8 @@ export function GraphQLTypeCard(props: {
308345
type={props.name}
309346
/>
310347
</div>
311-
{props.description ? <Description description={props.description} /> : null}
312348
</div>
349+
{props.description ? <DescriptionInline description={props.description} /> : null}
313350
</div>
314351
{Array.isArray(props.implements) && props.implements.length > 0 ? (
315352
<div className="flex flex-row items-center text-sm text-gray-500">
@@ -329,6 +366,7 @@ export function GraphQLTypeCard(props: {
329366
) : null}
330367
{props.usage && typeof props.totalRequests !== 'undefined' ? (
331368
<SchemaExplorerUsageStats
369+
kindLabel={props.kind}
332370
totalRequests={props.totalRequests}
333371
usage={props.usage}
334372
organizationSlug={props.organizationSlug}
@@ -529,83 +567,88 @@ export function GraphQLFields(props: {
529567

530568
return (
531569
<GraphQLTypeCardListItem key={field.name} index={i}>
532-
<div>
533-
{props.warnAboutUnusedArguments &&
534-
isUsed &&
535-
hasUnusedArguments &&
536-
showsUnusedSchema ? (
537-
<Tooltip>
538-
<TooltipContent>
539-
This field is used but the presented arguments are not.
540-
</TooltipContent>
541-
<TooltipTrigger>
542-
<span className="mr-1 text-sm text-orange-500">*</span>
543-
</TooltipTrigger>
544-
</Tooltip>
545-
) : null}
546-
{props.warnAboutDeprecatedArguments && !isDeprecated ? (
547-
<Tooltip>
548-
<TooltipContent>
549-
This field is not deprecated but the presented arguments are.
550-
</TooltipContent>
551-
<TooltipTrigger>
552-
<span className="mr-1 text-sm text-orange-500">*</span>
553-
</TooltipTrigger>
554-
</Tooltip>
555-
) : null}
556-
<DeprecationNote
557-
styleDeprecated={props.styleDeprecated}
558-
deprecationReason={field.deprecationReason}
559-
>
560-
<LinkToCoordinatePage
561-
organizationSlug={props.organizationSlug}
562-
projectSlug={props.projectSlug}
563-
targetSlug={props.targetSlug}
564-
coordinate={coordinate}
565-
className="font-semibold"
566-
>
567-
{field.name}
568-
</LinkToCoordinatePage>
569-
</DeprecationNote>
570-
{field.args.length > 0 ? (
571-
<GraphQLArguments
572-
organizationSlug={props.organizationSlug}
573-
projectSlug={props.projectSlug}
574-
targetSlug={props.targetSlug}
575-
styleDeprecated={props.styleDeprecated}
576-
parentCoordinate={coordinate}
577-
args={field.args}
578-
/>
579-
) : null}
580-
<span className="mr-1">:</span>
581-
<GraphQLTypeAsLink
582-
organizationSlug={props.organizationSlug}
583-
projectSlug={props.projectSlug}
584-
targetSlug={props.targetSlug}
585-
className="font-semibold text-gray-400"
586-
type={field.type}
587-
/>
588-
</div>
589-
<div className="flex flex-row items-center">
590-
{field.supergraphMetadata ? (
591-
<div className="ml-1">
592-
<SupergraphMetadataList
593-
targetSlug={props.targetSlug}
594-
projectSlug={props.projectSlug}
570+
<div className="w-full">
571+
<div className="flex w-full flex-row items-center justify-between">
572+
<div>
573+
{props.warnAboutUnusedArguments &&
574+
isUsed &&
575+
hasUnusedArguments &&
576+
showsUnusedSchema ? (
577+
<Tooltip>
578+
<TooltipContent>
579+
This field is used but the presented arguments are not.
580+
</TooltipContent>
581+
<TooltipTrigger>
582+
<span className="mr-1 text-sm text-orange-500">*</span>
583+
</TooltipTrigger>
584+
</Tooltip>
585+
) : null}
586+
{props.warnAboutDeprecatedArguments && !isDeprecated ? (
587+
<Tooltip>
588+
<TooltipContent>
589+
This field is not deprecated but the presented arguments are.
590+
</TooltipContent>
591+
<TooltipTrigger>
592+
<span className="mr-1 text-sm text-orange-500">*</span>
593+
</TooltipTrigger>
594+
</Tooltip>
595+
) : null}
596+
<DeprecationNote
597+
styleDeprecated={props.styleDeprecated}
598+
deprecationReason={field.deprecationReason}
599+
>
600+
<LinkToCoordinatePage
601+
organizationSlug={props.organizationSlug}
602+
projectSlug={props.projectSlug}
603+
targetSlug={props.targetSlug}
604+
coordinate={coordinate}
605+
className="font-semibold"
606+
>
607+
{field.name}
608+
</LinkToCoordinatePage>
609+
</DeprecationNote>
610+
{field.args.length > 0 ? (
611+
<GraphQLArguments
612+
organizationSlug={props.organizationSlug}
613+
projectSlug={props.projectSlug}
614+
targetSlug={props.targetSlug}
615+
styleDeprecated={props.styleDeprecated}
616+
parentCoordinate={coordinate}
617+
args={field.args}
618+
/>
619+
) : null}
620+
<span className="mr-1">:</span>
621+
<GraphQLTypeAsLink
595622
organizationSlug={props.organizationSlug}
596-
supergraphMetadata={field.supergraphMetadata}
623+
projectSlug={props.projectSlug}
624+
targetSlug={props.targetSlug}
625+
className="font-semibold text-gray-400"
626+
type={field.type}
597627
/>
598628
</div>
599-
) : null}
600-
{typeof totalRequests === 'number' ? (
601-
<SchemaExplorerUsageStats
602-
totalRequests={totalRequests}
603-
usage={field.usage}
604-
targetSlug={props.targetSlug}
605-
projectSlug={props.projectSlug}
606-
organizationSlug={props.organizationSlug}
607-
/>
608-
) : null}
629+
<div className="flex flex-row items-center">
630+
{field.supergraphMetadata ? (
631+
<div className="ml-1">
632+
<SupergraphMetadataList
633+
targetSlug={props.targetSlug}
634+
projectSlug={props.projectSlug}
635+
organizationSlug={props.organizationSlug}
636+
supergraphMetadata={field.supergraphMetadata}
637+
/>
638+
</div>
639+
) : null}
640+
{typeof totalRequests === 'number' ? (
641+
<SchemaExplorerUsageStats
642+
totalRequests={totalRequests}
643+
usage={field.usage}
644+
targetSlug={props.targetSlug}
645+
projectSlug={props.projectSlug}
646+
organizationSlug={props.organizationSlug}
647+
/>
648+
) : null}
649+
</div>
650+
</div>
651+
{field.description ? <DescriptionInline description={field.description} /> : null}
609652
</div>
610653
</GraphQLTypeCardListItem>
611654
);
@@ -666,39 +709,44 @@ export function GraphQLInputFields(props: {
666709
const coordinate = `${props.typeName}.${field.name}`;
667710
return (
668711
<GraphQLTypeCardListItem key={field.name} index={i}>
669-
<div className="text-gray-400">
670-
<DeprecationNote
671-
styleDeprecated={props.styleDeprecated}
672-
deprecationReason={field.deprecationReason}
673-
>
674-
<LinkToCoordinatePage
675-
organizationSlug={props.organizationSlug}
676-
projectSlug={props.projectSlug}
677-
targetSlug={props.targetSlug}
678-
coordinate={coordinate}
679-
className="font-semibold text-white"
680-
>
681-
{field.name}
682-
</LinkToCoordinatePage>
683-
</DeprecationNote>
684-
<span className="mr-1">:</span>
685-
<GraphQLTypeAsLink
686-
organizationSlug={props.organizationSlug}
687-
projectSlug={props.projectSlug}
688-
targetSlug={props.targetSlug}
689-
className="font-semibold"
690-
type={field.type}
691-
/>
712+
<div>
713+
<div className="flex w-full flex-row items-center justify-between">
714+
<div className="text-gray-400">
715+
<DeprecationNote
716+
styleDeprecated={props.styleDeprecated}
717+
deprecationReason={field.deprecationReason}
718+
>
719+
<LinkToCoordinatePage
720+
organizationSlug={props.organizationSlug}
721+
projectSlug={props.projectSlug}
722+
targetSlug={props.targetSlug}
723+
coordinate={coordinate}
724+
className="font-semibold text-white"
725+
>
726+
{field.name}
727+
</LinkToCoordinatePage>
728+
</DeprecationNote>
729+
<span className="mr-1">:</span>
730+
<GraphQLTypeAsLink
731+
organizationSlug={props.organizationSlug}
732+
projectSlug={props.projectSlug}
733+
targetSlug={props.targetSlug}
734+
className="font-semibold"
735+
type={field.type}
736+
/>
737+
</div>
738+
{typeof props.totalRequests === 'number' ? (
739+
<SchemaExplorerUsageStats
740+
totalRequests={props.totalRequests}
741+
usage={field.usage}
742+
targetSlug={props.targetSlug}
743+
projectSlug={props.projectSlug}
744+
organizationSlug={props.organizationSlug}
745+
/>
746+
) : null}
747+
</div>
748+
{field.description ? <DescriptionInline description={field.description} /> : null}
692749
</div>
693-
{typeof props.totalRequests === 'number' ? (
694-
<SchemaExplorerUsageStats
695-
totalRequests={props.totalRequests}
696-
usage={field.usage}
697-
targetSlug={props.targetSlug}
698-
projectSlug={props.projectSlug}
699-
organizationSlug={props.organizationSlug}
700-
/>
701-
) : null}
702750
</GraphQLTypeCardListItem>
703751
);
704752
})}

0 commit comments

Comments
 (0)