Skip to content
14 changes: 14 additions & 0 deletions packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { renderToStaticMarkup } from 'react-dom/server';
import { describe, it, expect } from 'vitest';
import { MethodBadge } from './MethodBadge';

const badgeText = (element: React.ReactElement): string =>
renderToStaticMarkup(element)
.replace(/<style[^>]*>[\s\S]*?<\/style>/g, '')
.replace(/<[^>]*>/g, '')
.trim();

describe('MethodBadge', () => {
it('renders the method uppercased', () => {
expect(renderToStaticMarkup(<MethodBadge method="post" />)).toContain('POST');
Expand All @@ -11,4 +17,12 @@ describe('MethodBadge', () => {
it('defaults to GET when no method is given', () => {
expect(renderToStaticMarkup(<MethodBadge method="" />)).toContain('GET');
});

it('renders the abbreviated method when short is set', () => {
expect(badgeText(<MethodBadge method="DELETE" short />)).toBe('DEL');
});

it('keeps short methods intact when short is set', () => {
expect(badgeText(<MethodBadge method="GET" short />)).toBe('GET');
});
});
23 changes: 15 additions & 8 deletions packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import React from 'react';
import cx from '../../utils/cx';
import { getMethodColorVar } from '../../theme/methodColors';
import { getShortMethod } from '../../utils/request';
import { StyledWrapper } from './StyledWrapper';

interface MethodBadgeProps {
method: string;
className?: string;
/** Render the abbreviated method (DELETE -> DEL, OPTIONS -> OPT) for tight spaces like the query bar. */
short?: boolean;
}

export const MethodBadge: React.FC<MethodBadgeProps> = ({ method, className }) => (
<StyledWrapper
className={['method-badge', className].filter(Boolean).join(' ')}
style={{ color: getMethodColorVar(method) }}
>
{(method || 'GET').toUpperCase()}
</StyledWrapper>
);
export const MethodBadge: React.FC<MethodBadgeProps> = ({ method, className, short = false }) => {
const resolvedMethod = method || 'GET';
return (
<StyledWrapper
className={cx('method-badge', className)}
style={{ color: getMethodColorVar(method) }}
>
{short ? getShortMethod(resolvedMethod) : resolvedMethod.toUpperCase()}
</StyledWrapper>
);
};

export default MethodBadge;
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { StyledWrapper } from './StyledWrapper';
import MenuDropdown from '../../../../../../ui/MenuDropdown';
import { getHttpMethod, getRequestUrl, getHttpParams } from '../../../../../../utils/schemaHelpers';
import { syncPathParams, syncQueryParams } from '../../../../../../utils/pathParams';
import { availableMethods, getMethodColorVar } from '../../../../../../theme/methodColors';
import { availableMethods } from '../../../../../../theme/methodColors';
import { MethodBadge } from '../../../../../MethodBadge/MethodBadge';
import { CopyButton } from '../../../../../../ui/CopyButton/CopyButton';
import { SendIcon } from '../../../../../../assets/icons';
Expand Down Expand Up @@ -56,24 +56,23 @@ const QueryBar: React.FC<QueryBarProps> = ({ item, onSendRequest, isLoading, onI

return (
<StyledWrapper>
<div className="method-select-wrapper">
<MenuDropdown
selectedItemId={method}
placement="bottom-start"
testId="method-select"
role="listbox"
items={availableMethods.map((m) => ({
id: m,
label: <span style={{ color: getMethodColorVar(m) }}>{m}</span>,
ariaLabel: m,
onClick: () => handleMethodChange(m)
}))}
>
<button type="button" className="method-select" aria-label="HTTP method">
<MethodBadge method={method} />
</button>
</MenuDropdown>
</div>
<MenuDropdown
selectedItemId={method}
placement="bottom-start"
testId="method-select"
role="listbox"
size="sm"
items={availableMethods.map((m) => ({
id: m,
label: <MethodBadge method={m} />,
ariaLabel: m,
onClick: () => handleMethodChange(m)
}))}
>
<button type="button" className="method-select" aria-label="HTTP method">
<MethodBadge method={method} short />
</button>
</MenuDropdown>

<input
type="text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ export const StyledWrapper = styled.div`
}
}

.method-select-wrapper {
flex-shrink: 0;
display: flex;
align-items: center;
}

.method-select {
appearance: none;
display: inline-flex;
Expand All @@ -44,6 +38,8 @@ export const StyledWrapper = styled.div`
border: none;
outline: none;
cursor: pointer;
padding-left: 0.5rem;
margin-left: -0.5rem;
}

.method-select .method-badge {
Expand Down
6 changes: 5 additions & 1 deletion packages/oc-docs/src/ui/MenuDropdown/MenuDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
MenuGroupStyle,
MenuItemExtraProps
} from './types';
import cx from '../../utils/cx';

const NAVIGATION_KEYS = ['ArrowDown', 'ArrowUp', 'Home', 'End', 'Escape'];
const ACTION_KEYS = ['Enter', ' '];
Expand Down Expand Up @@ -48,6 +49,8 @@ export interface MenuDropdownProps extends Omit<
placement?: Placement;
/** Optional className for the dropdown surface. */
className?: string;
/** Surface size variant: `'lg'` (default) or `'sm'` (compact, e.g. the method picker). */
size?: 'sm' | 'lg';
/** ID of the selected/active item to focus on open and mark active. */
selectedItemId?: MenuItemId | null;
/** ARIA role family: 'menu' (default) for action menus; 'listbox' for single-select value pickers. */
Expand Down Expand Up @@ -93,6 +96,7 @@ const MenuDropdown = forwardRef<MenuDropdownHandle, MenuDropdownProps>(
children,
placement = 'bottom-end',
className,
size = 'lg',
selectedItemId,
role = 'menu',
opened,
Expand Down Expand Up @@ -556,7 +560,7 @@ const MenuDropdown = forwardRef<MenuDropdownHandle, MenuDropdownProps>(
onCreate={onDropdownCreate}
icon={triggerElement}
placement={placement}
className={className}
className={cx(className, { 'menu-dropdown-sm': size === 'sm' })}
visible={isOpen}
onClickOutside={handleClickOutside}
{...dropdownProps}
Expand Down
15 changes: 15 additions & 0 deletions packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ export const StyledWrapper = styled.div`
max-width: unset !important;
padding: 0.25rem;

&.menu-dropdown-sm {
min-width: 6.875rem;
max-width: 9.375rem !important;
padding: 0.125rem;
background-color: var(--oc-background-base);
border-color: var(--border-color);
margin-top: 2px;

.dropdown-item {
padding: 0.25rem 0.6rem 0.25rem 0.25rem;
margin: 1px 0;
line-height: 1;
}
}

.menu-dropdown-list {
outline: none;
&:focus {
Expand Down
Loading