Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/oc-docs/e2e/components/playground.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export class PlaygroundComponent extends BaseComponent {
readonly keyValueTable = new KeyValueTableComponent(this.page);
readonly preRequestScriptEditor = new CodeEditorComponent(this.page, 'scripts-editor-pre-request');
readonly postResponseScriptEditor = new CodeEditorComponent(this.page, 'scripts-editor-post-response');
readonly bodyEditor = new CodeEditorComponent(this.page, 'body-editor');
readonly testsEditor = new CodeEditorComponent(this.page, 'tests-editor');

readonly header = this.page.getByTestId('playground-header');
readonly switcher = this.page.getByTestId('playground-dock-switcher');
Expand Down Expand Up @@ -94,6 +96,7 @@ export class PlaygroundComponent extends BaseComponent {

async openRequest(name: string): Promise<void> {
await this.treeItems.filter({ hasText: name }).first().click();
await this.view.waitFor({ state: 'visible' });
}

async openEnvironments(): Promise<void> {
Expand Down
49 changes: 49 additions & 0 deletions packages/oc-docs/e2e/tests/playground/editor-fill-height.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Locator } from '@playwright/test';
import { test, expect } from '../../playwright';

// The Body / Tests / Scripts editors in the request pane opt into `fillHeight`, so they stretch to
// fill the pane and stop ~1rem above its bottom, instead of a short fixed-height box that either
// leaves a large empty area or overflows the pane. We assert the editor's bottom sits just above
// the view's bottom: a fixed-height editor in this dock would overflow (negative gap) or fall well
// short (large gap).
async function expectFillsPane(editor: Locator, view: Locator): Promise<void> {
await expect(editor).toBeVisible();
const gapBelow = async (): Promise<number> => {
const e = await editor.boundingBox();
const v = await view.boundingBox();
if (!e || !v) return Number.NaN;
return Math.round(v.y + v.height - (e.y + e.height));
};
// Monaco relays out after the tab becomes active; retry until the gap settles to ~1rem.
await expect.poll(gapBelow, { message: 'editor should fill to ~1rem above the pane bottom' }).toBeLessThanOrEqual(48);
expect(await gapBelow()).toBeGreaterThanOrEqual(4);

const editorBox = await editor.boundingBox();
expect(editorBox!.height).toBeGreaterThan(40);
}

test.describe('Playground request editors fill height', () => {
// A wide viewport keeps the request-pane tabs on one row (no responsive "⋯" overflow),
// so selecting Body/Tests/Scripts is a direct click rather than a flaky dropdown step.
test.use({ viewport: { width: 1600, height: 900 } });

test.beforeEach(async ({ playground }) => {
await playground.open('bottom');
await playground.openRequest('echo json');
});

test('body editor fills the request pane', async ({ playground }) => {
await playground.selectTab('body');
await expectFillsPane(playground.bodyEditor.root, playground.view);
});

test('tests editor fills the request pane', async ({ playground }) => {
await playground.selectTab('tests');
await expectFillsPane(playground.testsEditor.root, playground.view);
});

test('pre-request script editor fills the request pane', async ({ playground }) => {
await playground.selectTab('scripts');
await expectFillsPane(playground.preRequestScriptEditor.root, playground.view);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import type { OpenCollection } from '@opencollection/types';
import Tabs from '../../../../../ui/Tabs/Tabs';
import TitleLabel from '../../../../TitleLabel/TitleLabel';
import { type KeyValueRow } from '../../../../../components/KeyValueTable/KeyValueTable';
import HeadersTab from '../Common/HeadersTab/HeadersTab';
import VariablesTab from '../Common/VariablesTab/VariablesTab';
Expand Down Expand Up @@ -154,10 +155,8 @@ const CollectionSettings: React.FC<CollectionSettingsProps> = ({ collection }) =

return (
<StyledWrapper>
<div className="collection-settings-header">
<h2 className="collection-settings-title">
{collection.info?.name || 'Collection Settings'}
</h2>
<div className="collection-settings-header mb-2">
<TitleLabel>{collection.info?.name || 'Collection Settings'}</TitleLabel>
{version && <span className="collection-settings-version">Version {version}</span>}
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,14 @@ export const StyledWrapper = styled.div`
height: 100%;
display: flex;
flex-direction: column;
padding: 0.75rem 1rem;
padding: 1.25rem 1.25rem 0;

.collection-settings-header {
display: flex;
align-items: baseline;
gap: 0.5rem;
}

.collection-settings-title {
color: var(--text-primary);
font-size: 0.875rem;
font-weight: 500;
line-height: 1;
letter-spacing: 0;
}

.collection-settings-version {
color: var(--text-tertiary);
font-size: 0.75rem;
Expand All @@ -32,7 +24,6 @@ export const StyledWrapper = styled.div`
flex: 1;
min-height: 0;
overflow-y: auto;
margin-top: 1.25rem;

.tabs {
gap: 1rem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ interface BodyTabProps {
body: RequestBody;
onItemChange: (item: HttpRequest) => void;
item: HttpRequest;
fillHeight?: boolean;
}

export const BodyTab: React.FC<BodyTabProps> = ({
body,
onItemChange,
item
item,
fillHeight = false
}) => {
const handleFormBodyChange = (formData: KeyValueRow[]) => {
const updatedBody = {
Expand Down Expand Up @@ -52,36 +54,39 @@ export const BodyTab: React.FC<BodyTabProps> = ({
};

return (
<div className="space-y-3">
<div className={`space-y-3${fillHeight ? ' h-full flex flex-col' : ''}`}>
{!body ? (
<div data-testid="body-empty" className="text-center py-6 border-2 border-dashed rounded" style={{ borderColor: 'var(--border-color)', color: 'var(--text-secondary)' }}>
No body content. Select a body type to add content.
</div>
) : 'data' in body && typeof body.data === 'string' ? (
<CodeEditor
value={body.data}
onChange={(value) => {
if ('data' in body && typeof body.data === 'string') {
onItemChange({
...item,
http: {
...item.http,
body: { ...body, data: value } as typeof body
}
});
<div className={fillHeight ? 'flex-1 min-h-0' : undefined}>
<CodeEditor
value={body.data}
onChange={(value) => {
if ('data' in body && typeof body.data === 'string') {
onItemChange({
...item,
http: {
...item.http,
body: { ...body, data: value } as typeof body
}
});
}
}}
language={
body.type === 'json'
? 'jsonc'
: body.type === 'xml'
? 'xml'
: body.type === 'sparql'
? 'sparql'
: 'text'
}
}}
language={
body.type === 'json'
? 'jsonc'
: body.type === 'xml'
? 'xml'
: body.type === 'sparql'
? 'sparql'
: 'text'
}
height="300px"
/>
height={fillHeight ? '100%' : '300px'}
testId="body-editor"
/>
</div>
) : Array.isArray(body) || (body?.type === 'form-urlencoded' && Array.isArray(body?.data)) ? (
(() => {
const formDataArray = Array.isArray(body) ? body : (body?.data || []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ interface ScriptsTabProps {
title?: string;
description?: string;
showTests?: boolean;
fillHeight?: boolean;
}

export const ScriptsTab: React.FC<ScriptsTabProps> = ({
scripts,
onScriptChange,
title,
description,
showTests = true
showTests = true,
fillHeight = false
}) => {
const [activeScriptTab, setActiveScriptTab] = useState<ScriptSubTab>('pre-request');

Expand All @@ -39,7 +41,7 @@ export const ScriptsTab: React.FC<ScriptsTabProps> = ({
value={scripts.preRequest || ''}
onChange={(value: string) => onScriptChange('preRequest', value)}
language="javascript"
height="300px"
height={fillHeight ? '100%' : '300px'}
hintsFor={['req', 'bru']}
active={activeScriptTab === 'pre-request'}
testId="scripts-editor-pre-request"
Expand All @@ -54,7 +56,7 @@ export const ScriptsTab: React.FC<ScriptsTabProps> = ({
value={scripts.postResponse || ''}
onChange={(value: string) => onScriptChange('postResponse', value)}
language="javascript"
height="300px"
height={fillHeight ? '100%' : '300px'}
hintsFor={['req', 'res', 'bru']}
active={activeScriptTab === 'post-response'}
testId="scripts-editor-post-response"
Expand All @@ -64,15 +66,15 @@ export const ScriptsTab: React.FC<ScriptsTabProps> = ({
];

return (
<StyledWrapper className="space-y-4">
<StyledWrapper className={`space-y-4${fillHeight ? ' h-full flex flex-col' : ''}`}>
{(Boolean(title) || Boolean(description)) && (
<div className="flex items-center justify-between mb-4">
{title && <span className="title text-sm font-semibold">{title}</span>}
{description && <span className="description text-xs leading-tight">{description}</span>}
</div>
)}

<div className="space-y-4">
<div className={`space-y-4${fillHeight ? ' flex-1 min-h-0 flex flex-col' : ''}`}>
<Tabs
tabs={tabs}
activeTab={activeScriptTab}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,28 @@ interface TestsTabProps {
onScriptChange: (scriptType: 'tests', value: string) => void;
title?: string;
description?: string;
fillHeight?: boolean;
}

export const TestsTab: React.FC<TestsTabProps> = ({ scripts, onScriptChange, title, description }) => {
export const TestsTab: React.FC<TestsTabProps> = ({ scripts, onScriptChange, title, description, fillHeight = false }) => {
return (
<StyledWrapper className="space-y-3">
<StyledWrapper className={`space-y-3${fillHeight ? ' h-full flex flex-col' : ''}`}>
{(Boolean(title) || Boolean(description)) && (
<div className="flex items-center justify-between mb-4">
{title && <span className="title text-sm font-semibold">{title}</span>}
{description && <span className="description text-xs leading-tight">{description}</span>}
</div>
)}
<CodeEditor
value={scripts.tests || ''}
onChange={(value) => onScriptChange('tests', value)}
language="javascript"
height="400px"
hintsFor={['req', 'res', 'bru']}
/>
<div className={fillHeight ? 'flex-1 min-h-0' : undefined}>
<CodeEditor
value={scripts.tests || ''}
onChange={(value) => onScriptChange('tests', value)}
language="javascript"
height={fillHeight ? '100%' : '400px'}
hintsFor={['req', 'res', 'bru']}
testId="tests-editor"
/>
</div>
</StyledWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Environment } from '@opencollection/types/config/environments';
import type { Variable } from '@opencollection/types/common/variables';
import KeyValueTable, { KeyValueRow } from '../../../../../components/KeyValueTable/KeyValueTable';
import Tabs from '../../../../../ui/Tabs/Tabs';
import TitleLabel from '../../../../TitleLabel/TitleLabel';
import { EmptyState } from '../../../../../ui/EmptyState/EmptyState';
import { StyledWrapper } from './StyledWrapper';
import { EnvironmentLabel } from '../../../../EnvironmentLabel/EnvironmentLabel';
Expand Down Expand Up @@ -198,7 +199,7 @@ const EnvironmentsView: React.FC<EnvironmentsViewProps> = ({ collection, compact
return (
<StyledWrapper>
<div className="env-header">
<h2 className="env-title">Environments</h2>
<TitleLabel>Environments</TitleLabel>
</div>

<div className="env-pills">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,10 @@ export const StyledWrapper = styled.div`
background-color: var(--oc-background-base);

.env-header {
padding: 1rem 1.5rem 0;
padding: 1.25rem 1.25rem 0;
flex-shrink: 0;
}

.env-title {
font-size: 1.125rem;
font-weight: 600;
font-size: 0.8125rem;
color: var(--oc-text);
}

.env-message {
flex: 1;
display: flex;
Expand All @@ -34,7 +27,7 @@ export const StyledWrapper = styled.div`
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
padding: 0.5rem 1.25rem 1rem 1.25rem;
}

.env-pill {
Expand Down Expand Up @@ -68,7 +61,7 @@ export const StyledWrapper = styled.div`
display: flex;
flex-direction: column;
min-height: 0;
padding: 0 1.5rem 1.5rem;
padding: 0 1.25rem 1.25rem;
}

.env-tabs-area .tab-content {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ describe('ExampleView', () => {
expect(root.querySelector('textarea')).toBeNull();
});

it('renders the example-name suffix after a named example', () => {
const root = useRenderToDom(<ExampleView request={request} example={example} />);
expect(query(root, '.example-view-name-suffix').text).toContain('/ Example');
});

it('shows an empty message when the request has no params, headers or body', () => {
const root = useRenderToDom(<ExampleView request={request} example={{ ...example, request: {} }} />);
const empty = root.querySelector('[data-testid="example-view-request-empty"]');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ export const ExampleView: React.FC<ExampleViewProps> = ({ request, example, orie
return (
<StyledWrapper data-testid="example-view">
<div className="example-view-head">
<span className="example-view-name">
{example.name || 'Example'}
{example.name && <span className="example-view-name-suffix">/ Example</span>}
</span>
<span className="example-view-name">{example.name || 'Example'}</span>
{description && <span className="example-view-desc">{description}</span>}
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const StyledWrapper = styled.div`
display: flex;
flex-direction: column;
overflow: hidden;
padding: 1rem 1rem 0;
padding: 1.25rem 1.25rem 0;
background: var(--bg-primary);

.example-view-head {
Expand All @@ -17,15 +17,11 @@ export const StyledWrapper = styled.div`
margin-bottom: 0.75rem;
}
.example-view-name {
font-size: 0.8125rem;
font-size: var(--oc-font-size-md);
line-height: 1.4;
font-weight: 600;
color: var(--text-primary);
}
.example-view-name-suffix {
margin-left: 0.375rem;
font-weight: 400;
color: var(--text-tertiary);
}
.example-view-desc {
color: var(--text-secondary);
font-size: 0.8125rem;
Expand Down
Loading
Loading