From 7af1b93f5345d6ed27d1ad4741090e83439db528 Mon Sep 17 00:00:00 2001 From: Sundram Gupta Date: Wed, 22 Jul 2026 18:40:57 +0530 Subject: [PATCH 1/6] feat(oc-docs): match playground method dropdown and url bar to Bruno app Align the playground query bar's HTTP method control with the Bruno app for visual parity: - Method dropdown items now render as MethodBadge (bold, mono, uppercase, method-coloured) instead of plain text, matching the trigger badge. - Constrain the method dropdown surface to 110-150px width, tighten item spacing (line-height, padding, margin) and surface padding. - Match the dropdown surface background and border to the query bar (app base background and --border-color) so it blends in dark mode. - Left-align the dropdown to the url box border with a 1px gap below it via Tippy offset. - Abbreviate long methods in the url bar trigger (DELETE -> DEL, OPTIONS -> OPT) through a new MethodBadge `short` prop. - Reduce the method trigger's left/right padding to match the docs url bar. --- .../MethodBadge/MethodBadge.spec.tsx | 10 +++++++++ .../components/MethodBadge/MethodBadge.tsx | 22 ++++++++++++------- .../PlaygroundView/QueryBar/QueryBar.tsx | 10 +++++---- .../PlaygroundView/QueryBar/StyledWrapper.ts | 4 ++-- .../src/ui/MenuDropdown/StyledWrapper.ts | 14 ++++++++++++ 5 files changed, 46 insertions(+), 14 deletions(-) diff --git a/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx b/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx index 1fed42bf..cb4acd8a 100644 --- a/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx +++ b/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx @@ -11,4 +11,14 @@ describe('MethodBadge', () => { it('defaults to GET when no method is given', () => { expect(renderToStaticMarkup()).toContain('GET'); }); + + it('renders the abbreviated method when short is set', () => { + const html = renderToStaticMarkup(); + expect(html).toContain('>DEL<'); + expect(html).not.toContain('DELETE'); + }); + + it('keeps short methods intact when short is set', () => { + expect(renderToStaticMarkup()).toContain('>GET<'); + }); }); diff --git a/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx b/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx index 1a2e385d..3a46a048 100644 --- a/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx +++ b/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx @@ -1,19 +1,25 @@ import React from 'react'; 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 = ({ method, className }) => ( - - {(method || 'GET').toUpperCase()} - -); +export const MethodBadge: React.FC = ({ method, className, short = false }) => { + const resolvedMethod = method || 'GET'; + return ( + + {short ? getShortMethod(resolvedMethod) : resolvedMethod.toUpperCase()} + + ); +}; export default MethodBadge; diff --git a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx index 3d4be258..93374f0e 100644 --- a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx +++ b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx @@ -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'; @@ -59,17 +59,19 @@ const QueryBar: React.FC = ({ item, onSendRequest, isLoading, onI ({ id: m, - label: {m}, + label: , ariaLabel: m, onClick: () => handleMethodChange(m) }))} > @@ -79,7 +81,7 @@ const QueryBar: React.FC = ({ item, onSendRequest, isLoading, onI value={url} onChange={(e) => handleUrlChange(e.target.value)} placeholder="Enter request URL" - className="flex-1 px-3 text-xs font-normal" + className="flex-1 pr-3 text-xs font-normal" style={{ fontFamily: 'var(--font-mono)', fontSize: '12px', diff --git a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts index 421efea2..3e218c60 100644 --- a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts +++ b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; export const StyledWrapper = styled.div` - gap: 0.5rem; + gap: 0.25rem; padding: 0.25rem 0.375rem; border: 1px solid var(--border-color); border-radius: var(--oc-radius); @@ -42,7 +42,7 @@ export const StyledWrapper = styled.div` position: relative; display: flex; align-items: center; - padding-left: 0.375rem; + padding-left: 0.125rem; } .method-select { diff --git a/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts b/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts index 91bf9490..56ce42c8 100644 --- a/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts +++ b/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts @@ -24,6 +24,20 @@ export const StyledWrapper = styled.div` max-width: unset !important; padding: 0.25rem; + &.method-menu-dropdown { + min-width: 6.875rem; + max-width: 9.375rem !important; + padding: 0.125rem; + background-color: var(--oc-background-base); + border-color: var(--border-color); + + .dropdown-item { + padding: 0.25rem 0.6rem 0.25rem 0.25rem; + margin: 1px 0; + line-height: 1; + } + } + .menu-dropdown-list { outline: none; &:focus { From 0be3fe7d7d4f30d47f1bc854359765698febf0eb Mon Sep 17 00:00:00 2001 From: Sundram Gupta Date: Wed, 22 Jul 2026 19:24:23 +0530 Subject: [PATCH 2/6] refactor(oc-docs): move method dropdown styling out of shared MenuDropdown Address review: the method-menu-dropdown overrides lived in the shared MenuDropdown StyledWrapper. Move them to QueryBar via an Emotion Global so the query-bar-specific styling is owned by QueryBar. A Global is used (rather than QueryBar's nested StyledWrapper) because the dropdown surface is portaled to , so a nested selector can't reach it. --- .../Views/PlaygroundView/QueryBar/QueryBar.tsx | 4 +++- .../PlaygroundView/QueryBar/StyledWrapper.ts | 17 +++++++++++++++++ .../src/ui/MenuDropdown/StyledWrapper.ts | 14 -------------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx index 93374f0e..aefa7c5e 100644 --- a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx +++ b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; +import { Global } from '@emotion/react'; import type { HttpRequest } from '@opencollection/types/requests/http'; -import { StyledWrapper } from './StyledWrapper'; +import { StyledWrapper, methodDropdownStyles } from './StyledWrapper'; import MenuDropdown from '../../../../../../ui/MenuDropdown'; import { getHttpMethod, getRequestUrl, getHttpParams } from '../../../../../../utils/schemaHelpers'; import { syncPathParams, syncQueryParams } from '../../../../../../utils/pathParams'; @@ -55,6 +56,7 @@ const QueryBar: React.FC = ({ item, onSendRequest, isLoading, onI return ( +
Date: Wed, 22 Jul 2026 20:21:37 +0530 Subject: [PATCH 3/6] fix(oc-docs): raise method dropdown item selector specificity The method-menu-dropdown item overrides tied the base .dropdown-item selector (0,2,0) and lost on source order, so items fell back to the shared padding. Qualify with .dropdown to win the cascade. --- .../Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts index 59549cec..b7eb2bf0 100644 --- a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts +++ b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/StyledWrapper.ts @@ -104,7 +104,7 @@ export const methodDropdownStyles = css` border-color: var(--border-color); } - .method-menu-dropdown .dropdown-item { + .dropdown.method-menu-dropdown .dropdown-item { padding: 0.25rem 0.6rem 0.25rem 0.25rem; margin: 1px 0; line-height: 1; From 09343ca3e45e08ac6b48302919342906875c027f Mon Sep 17 00:00:00 2001 From: Vineet Sharma Date: Thu, 23 Jul 2026 12:28:47 +0530 Subject: [PATCH 4/6] Improve the implementation in MenuDropdown --- .../PlaygroundView/QueryBar/QueryBar.tsx | 41 ++++++++----------- .../PlaygroundView/QueryBar/StyledWrapper.ts | 25 +---------- .../src/ui/MenuDropdown/MenuDropdown.tsx | 6 ++- .../src/ui/MenuDropdown/StyledWrapper.ts | 14 +++++++ 4 files changed, 39 insertions(+), 47 deletions(-) diff --git a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx index 2d295ff7..9182baf1 100644 --- a/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx +++ b/packages/oc-docs/src/components/Playground/Content/Views/PlaygroundView/QueryBar/QueryBar.tsx @@ -1,7 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { Global } from '@emotion/react'; import type { HttpRequest } from '@opencollection/types/requests/http'; -import { StyledWrapper, methodDropdownStyles } from './StyledWrapper'; +import { StyledWrapper } from './StyledWrapper'; import MenuDropdown from '../../../../../../ui/MenuDropdown'; import { getHttpMethod, getRequestUrl, getHttpParams } from '../../../../../../utils/schemaHelpers'; import { syncPathParams, syncQueryParams } from '../../../../../../utils/pathParams'; @@ -57,27 +56,23 @@ const QueryBar: React.FC = ({ item, onSendRequest, isLoading, onI return ( - -
- ({ - id: m, - label: , - ariaLabel: m, - onClick: () => handleMethodChange(m) - }))} - > - - -
+ ({ + id: m, + label: , + ariaLabel: m, + onClick: () => handleMethodChange(m) + }))} + > + + ( children, placement = 'bottom-end', className, + size = 'lg', selectedItemId, role = 'menu', opened, @@ -556,7 +560,7 @@ const MenuDropdown = forwardRef( onCreate={onDropdownCreate} icon={triggerElement} placement={placement} - className={className} + className={cx(className, { 'menu-dropdown-sm': size === 'sm' })} visible={isOpen} onClickOutside={handleClickOutside} {...dropdownProps} diff --git a/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts b/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts index 91bf9490..b3500a8b 100644 --- a/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts +++ b/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts @@ -24,6 +24,20 @@ 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); + + .dropdown-item { + padding: 0.25rem 0.6rem 0.25rem 0.25rem; + margin: 1px 0; + line-height: 1; + } + } + .menu-dropdown-list { outline: none; &:focus { From b5d3dcb4ed0bb96b5b9a1d711d4b5a50b7b921f2 Mon Sep 17 00:00:00 2001 From: Sundram Gupta Date: Thu, 23 Jul 2026 18:11:54 +0530 Subject: [PATCH 5/6] style(oc-docs): add 2px top gap to compact method dropdown --- packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts b/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts index b3500a8b..436f65e7 100644 --- a/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts +++ b/packages/oc-docs/src/ui/MenuDropdown/StyledWrapper.ts @@ -30,6 +30,7 @@ export const StyledWrapper = styled.div` 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; From 86eaa824287fbd9cbc963428863972870206d75c Mon Sep 17 00:00:00 2001 From: Sundram Gupta Date: Thu, 23 Jul 2026 19:00:16 +0530 Subject: [PATCH 6/6] refactor(oc-docs): address review on MethodBadge - Use the cx util for className composition instead of a manual filter/join. - Assert MethodBadge short-mode output by rendered text (strip markup) rather than matching the raw HTML string. --- .../src/components/MethodBadge/MethodBadge.spec.tsx | 12 ++++++++---- .../src/components/MethodBadge/MethodBadge.tsx | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx b/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx index cb4acd8a..81424585 100644 --- a/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx +++ b/packages/oc-docs/src/components/MethodBadge/MethodBadge.spec.tsx @@ -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(/]*>[\s\S]*?<\/style>/g, '') + .replace(/<[^>]*>/g, '') + .trim(); + describe('MethodBadge', () => { it('renders the method uppercased', () => { expect(renderToStaticMarkup()).toContain('POST'); @@ -13,12 +19,10 @@ describe('MethodBadge', () => { }); it('renders the abbreviated method when short is set', () => { - const html = renderToStaticMarkup(); - expect(html).toContain('>DEL<'); - expect(html).not.toContain('DELETE'); + expect(badgeText()).toBe('DEL'); }); it('keeps short methods intact when short is set', () => { - expect(renderToStaticMarkup()).toContain('>GET<'); + expect(badgeText()).toBe('GET'); }); }); diff --git a/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx b/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx index 3a46a048..26aa260b 100644 --- a/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx +++ b/packages/oc-docs/src/components/MethodBadge/MethodBadge.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import cx from '../../utils/cx'; import { getMethodColorVar } from '../../theme/methodColors'; import { getShortMethod } from '../../utils/request'; import { StyledWrapper } from './StyledWrapper'; @@ -14,7 +15,7 @@ export const MethodBadge: React.FC = ({ method, className, sho const resolvedMethod = method || 'GET'; return ( {short ? getShortMethod(resolvedMethod) : resolvedMethod.toUpperCase()}