chore(tailwind): Move grpc-method-dropdown to tailwind (#7074)

* grpc-method-dropdown tailwind

* fix disabled prop
This commit is contained in:
James Gatz 2024-02-09 16:11:21 +01:00 committed by GitHub
parent c0707c884f
commit 45edba1269
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 98 additions and 95 deletions

View File

@ -26,7 +26,7 @@ test('can send gRPC requests with reflection', async ({ app, page }) => {
await page.getByTestId('button-server-reflection').click(); await page.getByTestId('button-server-reflection').click();
await page.getByRole('button', { name: 'Select Method' }).click(); await page.getByRole('button', { name: 'Select Method' }).click();
await page.getByRole('menuitem', { name: 'RouteGuide/GetFeature' }).click(); await page.getByRole('option', { name: 'RouteGuide/GetFeature' }).click();
await page.getByRole('tab', { name: 'Unary' }).click(); await page.getByRole('tab', { name: 'Unary' }).click();
await page.getByRole('button', { name: 'Send' }).click(); await page.getByRole('button', { name: 'Send' }).click();

View File

@ -1,16 +1,8 @@
import React, { FunctionComponent } from 'react'; import React, { Fragment, FunctionComponent } from 'react';
import styled from 'styled-components'; import { Button, Header, ListBox, ListBoxItem, Popover, Section, Select, SelectValue } from 'react-aria-components';
import type { GrpcMethodInfo } from '../../../../main/ipc/grpc'; import type { GrpcMethodInfo, GrpcMethodType } from '../../../../main/ipc/grpc';
import { Dropdown, DropdownButton, DropdownItem, DropdownSection, ItemContent } from '../../base/dropdown'; import { Icon } from '../../icon';
import { GrpcMethodTag } from '../../tags/grpc-method-tag';
import { Tooltip } from '../../tooltip';
const DropdownMethodButtonLabel = styled.div({
display: 'flex',
alignItems: 'center',
gap: 'var(--padding-xs)',
});
interface Props { interface Props {
disabled?: boolean; disabled?: boolean;
@ -22,6 +14,13 @@ const PROTO_PATH_REGEX = /^\/(?:(?<package>[\w.]+)\.)?(?<service>\w+)\/(?<method
export const NO_PACKAGE_KEY = 'no-package'; export const NO_PACKAGE_KEY = 'no-package';
const GrpcMethodTypeAcronym = {
unary: 'U',
server: 'SS',
client: 'CS',
bidi: 'BD',
} as const;
function groupBy(list: {}[], keyGetter: (item: any) => string): Record<string, any[]> { function groupBy(list: {}[], keyGetter: (item: any) => string): Record<string, any[]> {
const map = new Map(); const map = new Map();
list.forEach(item => { list.forEach(item => {
@ -48,9 +47,6 @@ export const getShortGrpcPath = (fullPath: string): string => {
const methodName = result?.groups?.method; const methodName = result?.groups?.method;
return packageName && serviceName && methodName ? `/${serviceName}/${methodName}` : fullPath; return packageName && serviceName && methodName ? `/${serviceName}/${methodName}` : fullPath;
}; };
const NormalCase = styled.span`
text-transform: initial;
`;
export const GrpcMethodDropdown: FunctionComponent<Props> = ({ export const GrpcMethodDropdown: FunctionComponent<Props> = ({
disabled, disabled,
@ -59,61 +55,97 @@ export const GrpcMethodDropdown: FunctionComponent<Props> = ({
handleChange, handleChange,
}) => { }) => {
const groupedByPkg = groupGrpcMethodsByPackage(methods); const groupedByPkg = groupGrpcMethodsByPackage(methods);
const sections = Object.entries(groupedByPkg).map(([name, pkg]) => ({
id: name,
name: name !== NO_PACKAGE_KEY ? `pkg: ${name}` : 'No package',
display_name: name !== NO_PACKAGE_KEY ? `pkg: ${name}` : 'No package',
items: pkg.map(({ type, fullPath, example }) => ({
id: fullPath,
fullPath,
display_name: getShortGrpcPath(fullPath),
type,
example,
isDisabled: disabled,
})),
}));
const selectedPath = selectedMethod?.fullPath; const selectedPath = selectedMethod?.fullPath;
return ( return (
<Dropdown <Select
aria-label='Select gRPC method dropdown' aria-label="Select gRPC method"
className="tall wide" name="method"
onSelectionChange={key => {
handleChange(key.toString());
}}
className="h-full"
selectedKey={selectedPath}
isDisabled={methods.length === 0} isDisabled={methods.length === 0}
triggerButton={
<DropdownButton
size='medium'
className='tall wide'
removeBorderRadius
removePaddings={false}
disableHoverBehavior={false}
isDisabled={methods.length === 0}
style={{ maxWidth: '250px' }}
>
<Tooltip
message={selectedPath || 'Add proto file or use server reflection'}
position="bottom"
delay={500}
style={{ maxWidth: '240px', display: 'flex', alignItems: 'center' }}
>
<span style={{ maxWidth: '240px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{!selectedPath ? 'Select Method' : getShortGrpcPath(selectedPath)}
</span>
<i className="fa fa-caret-down pad-left-sm" />
</Tooltip>
</DropdownButton>
}
> >
{Object.entries(groupedByPkg).map(([name, pkg]) => ( <Button className="px-4 py-1 disabled:bg-[--hl-xs] h-full disabled:cursor-not-allowed font-semibold flex items-center justify-center gap-2 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] data-[pressed]:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm">
<DropdownSection <SelectValue<{
key={name} id: string;
aria-label='Select gRPC method section' fullPath: string;
title={name !== NO_PACKAGE_KEY && <NormalCase>pkg: {name}</NormalCase>} display_name: string;
type: GrpcMethodType;
example: Record<string, any> | undefined;
}>
className="flex truncate items-center justify-center gap-2"
> >
{pkg.map(({ type, fullPath }) => ( {({ selectedItem }) => {
<DropdownItem if (!selectedItem) {
key={fullPath} return (
aria-label={fullPath} <Fragment>
> <span>
<ItemContent {selectedPath ? getShortGrpcPath(selectedPath) : 'Select Method'}
isDisabled={disabled} </span>
isSelected={fullPath === selectedPath} </Fragment>
onClick={() => handleChange(fullPath)} );
> }
<Tooltip message={fullPath} position="right" delay={500}>
<DropdownMethodButtonLabel><GrpcMethodTag methodType={type} /> {getShortGrpcPath(fullPath)}</DropdownMethodButtonLabel> return (
</Tooltip> <Fragment>
</ItemContent> {selectedItem.display_name}
</DropdownItem> </Fragment>
))} );
</DropdownSection> }}
))} </SelectValue>
</Dropdown> <Icon icon="caret-down" />
</Button>
<Popover className="min-w-max max-w-xs">
<ListBox
items={sections}
className="border select-none text-sm min-w-max border-solid border-[--hl-sm] shadow-lg bg-[--color-bg] py-2 rounded-md overflow-y-auto max-h-[85vh] focus:outline-none"
>
{item => (
<Section key={item.id}>
<Header className='flex px-[--padding-md] items-center gap-2 text-[--hl-md]'><span>{item.display_name}</span><span className='bg-[--hl-md] h-[1px] flex-1' /></Header>
{item.items.map(grpcMethod => (
<ListBoxItem
id={grpcMethod.id}
key={grpcMethod.id}
className="flex gap-2 px-[--padding-md] aria-selected:font-bold items-center text-[--color-font] h-[--line-height-xs] w-full text-md whitespace-nowrap bg-transparent hover:bg-[--hl-sm] disabled:cursor-not-allowed focus:bg-[--hl-xs] focus:outline-none transition-colors"
aria-label={grpcMethod.display_name}
textValue={grpcMethod.display_name}
value={grpcMethod}
>
{({ isSelected }) => (
<Fragment>
<em>{GrpcMethodTypeAcronym[grpcMethod.type]}</em>
{grpcMethod.display_name}
{isSelected && (
<Icon
icon="check"
className="text-[--color-success] justify-self-end"
/>
)}
</Fragment>
)}
</ListBoxItem>
))}
</Section>
)}
</ListBox>
</Popover>
</Select >
); );
}; };

View File

@ -1,29 +0,0 @@
import React, { FunctionComponent } from 'react';
import styled from 'styled-components';
import type { GrpcMethodType } from '../../../main/ipc/grpc';
import { GrpcMethodTypeName } from '../panes/grpc-request-pane';
import { Tooltip } from '../tooltip';
interface Props {
methodType: GrpcMethodType;
}
const StyledTag = styled.div`
width: 1.5em;
text-align: right;
`;
const GrpcMethodTypeAcronym = {
unary: 'U',
server: 'SS',
client: 'CS',
bidi: 'BD',
} as const;
export const GrpcMethodTag: FunctionComponent<Props> = ({ methodType }) => (
<Tooltip message={GrpcMethodTypeName[methodType]} position="left" delay={500}>
<StyledTag>
<em>{GrpcMethodTypeAcronym[methodType]}</em>
</StyledTag>
</Tooltip>
);