Skip to content

Commit

Permalink
Merge branch 'develop' into perf/modify-initial-entry
Browse files Browse the repository at this point in the history
  • Loading branch information
CurryYangxx committed Jun 24, 2024
2 parents 9b08cc8 + fbf2b5d commit f364d71
Show file tree
Hide file tree
Showing 20 changed files with 472 additions and 656 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ With Insomnia you can:
- **Debug APIs** using the most popular protocols and formats.
- **Design APIs** using the native OpenAPI editor and visual preview.
- **Test APIs** using native test suites.
- **Mock APIs** will be coming soon in November 2023.
- **Mock APIs** using a server and routes.
- **Build CI/CD pipelines** using the native Insomnia CLI for linting and testing.
- **Collaborate with others** using the many collaboration features to share your projects.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ test.describe('Debug-Sidebar', async () => {
await requestLocator.click();
await requestLocator.getByLabel('Request Actions').click();
await page.getByRole('menuitemradio', { name: 'Settings' }).click();
await page.getByRole('tab', { name: 'Preview' }).click();
// Close settings modal
await page.locator('.app').press('Escape');

Expand All @@ -37,22 +36,19 @@ test.describe('Debug-Sidebar', async () => {
await ws.click();
await ws.getByLabel('Request Actions').click();
await page.getByRole('menuitemradio', { name: 'Settings' }).click();
await page.getByRole('tab', { name: 'Preview' }).click();
// Close settings modal
await page.locator('.app').press('Escape');

const gql = page.getByLabel('Request Collection').getByRole('row', { name: 'example graphql' });
await gql.click();
await gql.getByLabel('Request Actions').click();
await page.getByRole('menuitemradio', { name: 'Settings' }).click();
await page.getByRole('tab', { name: 'Preview' }).click();
// Close settings modal
await page.locator('.app').press('Escape');
const folderLocator = page.getByLabel('Request Collection').getByRole('row', { name: 'test folder' });
await folderLocator.click();
await folderLocator.getByLabel('Request Group Actions').click();
await page.getByRole('menuitemradio', { name: 'Settings' }).click();
await page.getByRole('tab', { name: 'Preview' }).click();
// Close settings modal
await page.locator('.app').press('Escape');
});
Expand Down Expand Up @@ -85,10 +81,6 @@ test.describe('Debug-Sidebar', async () => {
await page.locator('text=Done').click();
});

test.skip('Use Copy as Curl for a request', async ({}) => {
// TODO: implement this in a separate ticket
});

test('Pin a Request', async ({ page }) => {
const requestLocator = page.getByLabel('Request Collection').getByRole('row', { name: 'example http' });
await requestLocator.click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ test('Request tabs', async ({ page }) => {
await page.getByRole('tab', { name: 'Params' }).click();
await page.getByRole('tab', { name: 'Headers' }).click();
await page.getByRole('tab', { name: 'Docs' }).click();
await page.locator('text=Add Description').click();
await page.locator('[data-testid="CodeEditor"] pre[role="presentation"]:has-text("")').click();
await page.locator('textarea').nth(1).fill('new request');
await page.getByTestId('CodeEditor').getByRole('textbox').fill('some docs');
await page.getByRole('tab', { name: 'Preview' }).click();
});

test('WS tabs', async ({ page }) => {
Expand All @@ -33,7 +32,6 @@ test('WS tabs', async ({ page }) => {
await page.getByRole('tab', { name: 'Params' }).click();
await page.getByRole('tab', { name: 'Headers' }).click();
await page.getByRole('tab', { name: 'Docs' }).click();
await page.getByRole('button', { name: 'Add Description' }).click();
await page.locator('[data-testid="CodeEditor"] pre[role="presentation"]:has-text("")').click();
await page.locator('textarea').nth(1).fill('new wss');
await page.getByTestId('CodeEditor').getByRole('textbox').fill('some docs');
await page.getByRole('tab', { name: 'Preview' }).click();
});
4 changes: 2 additions & 2 deletions packages/insomnia/src/models/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ function expectParentToBeProject(parentId?: string | null) {

export const SCRATCHPAD_WORKSPACE_ID = 'wrk_scratchpad';

export function isScratchpad(workspace: Workspace) {
return workspace._id === SCRATCHPAD_WORKSPACE_ID;
export function isScratchpad(workspace?: Workspace) {
return workspace?._id === SCRATCHPAD_WORKSPACE_ID;
}

export const scopeToActivity = (scope: WorkspaceScope) => {
Expand Down
390 changes: 245 additions & 145 deletions packages/insomnia/src/ui/components/environment-picker.tsx

Large diffs are not rendered by default.

140 changes: 60 additions & 80 deletions packages/insomnia/src/ui/components/markdown-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,23 @@
import React, { forwardRef, ReactElement, useCallback, useState } from 'react';
import styled from 'styled-components';
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';

import { PanelContainer, TabItem, Tabs } from './base/tabs';
import { CodeEditor, CodeEditorHandle } from './codemirror/code-editor';
import { ErrorBoundary } from './error-boundary';
import { Icon } from './icon';
import { MarkdownPreview } from './markdown-preview';

interface Props {
onChange: Function;
defaultValue: string;
placeholder?: string;
defaultPreviewMode?: boolean;
className?: string;
mode?: string;
tall?: boolean;
}

interface MarkdownEditProps {
withDynamicHeight: boolean;
}

const Wrapper = styled.div({
border: '1px solid var(--hl-md)',
boxSizing: 'border-box',
});

const MarkdownEdit = styled.div<MarkdownEditProps>(({ withDynamicHeight }) => ({
padding: 'var(--padding-xs) var(--padding-sm)',

...withDynamicHeight ? {
'.CodeMirror-scroll': {
// Not sure why this style doesn't work on .CodeMirror...
maxHeight: '30vh',
},

'.input': {
height: 'auto !important',
},
} : {
height: '100%',
display: 'grid',
gridTemplateRows: '1fr auto',

'.input': {
height: '100%',
},
},
}));

const MarkdownPreiview = styled.div({
maxHeight: '35vh',
padding: 'var(--padding-sm)',
overflow: 'auto',
});

export const MarkdownEditor = forwardRef<CodeEditorHandle, Props>(({
mode,
placeholder,
defaultPreviewMode,
className,
tall,
defaultValue,
onChange,
Expand All @@ -71,43 +31,63 @@ export const MarkdownEditor = forwardRef<CodeEditorHandle, Props>(({
}, [onChange]);

return (
<Wrapper className={className}>
<Tabs
aria-label="Markdown editor tabs"
defaultSelectedKey={defaultPreviewMode ? 'preview' : 'write' }
>
<TabItem key="write" title="Write">
<MarkdownEdit withDynamicHeight={!tall}>
<div className='form-control form-control--outlined'>
<CodeEditor
id="markdown-editor"
ref={ref}
hideGutters
hideLineNumbers
dynamicHeight={!tall}
showPrettifyButton
noStyleActiveLine
enableNunjucks
mode={mode || 'text/x-markdown'}
placeholder={placeholder}
defaultValue={markdown}
onChange={handleChange}
/>
</div>
<div className='txt-sm italic faint'>
Styling with Markdown is supported
</div>
</MarkdownEdit>
</TabItem>
<TabItem key="preview" title="Preview">
<MarkdownPreiview>
<PanelContainer className="markdown-editor__preview">
<MarkdownPreview markdown={markdown} />
</PanelContainer>
</MarkdownPreiview>
</TabItem>
</Tabs>
</Wrapper>
<Tabs
className="w-full h-full flex flex-col overflow-hidden"
aria-label="Markdown editor tabs"
defaultSelectedKey={defaultValue ? 'preview' : 'write'}
disabledKeys={[defaultValue ? '' : 'preview']}
>
<TabList className="w-full flex-shrink-0 overflow-x-auto border-solid border-b border-b-[--hl-md] px-2 bg-[--color-bg] flex items-center gap-2 h-[--line-height-sm]" aria-label="Request scripts tabs">
<Tab
className="rounded-md flex-shrink-0 h-[--line-height-xxs] text-sm flex items-center justify-between cursor-pointer w-[10.5rem] outline-none select-none px-2 py-1 hover:bg-[rgba(var(--color-surprise-rgb),50%)] text-[--hl] aria-selected:text-[--color-font-surprise] hover:text-[--color-font-surprise] aria-selected:bg-[rgba(var(--color-surprise-rgb),40%)] transition-colors duration-300"
id="write"
>
<div className='flex flex-1 items-center gap-2'>
<Icon icon="arrow-right-to-bracket" />
<span>Write</span>
</div>
</Tab>
<Tab
className="rounded-md flex-shrink-0 h-[--line-height-xxs] text-sm flex items-center justify-between cursor-pointer w-[10.5rem] outline-none select-none px-2 py-1 hover:bg-[rgba(var(--color-surprise-rgb),50%)] text-[--hl] aria-selected:text-[--color-font-surprise] hover:text-[--color-font-surprise] aria-selected:bg-[rgba(var(--color-surprise-rgb),40%)] transition-colors duration-300"
id="preview"
>
<div className='flex flex-1 items-center gap-2'>
<Icon icon="arrow-right-from-bracket" />
<span>Preview</span>
</div>
</Tab>
</TabList>
<TabPanel className="w-full flex-1 overflow-hidden m-2" id='write'>
<ErrorBoundary
errorClassName="tall wide vertically-align font-error pad text-center"
>
<div className='h-full flex flex-col divide-y divide-solid divide-[--hl-md]'>
<CodeEditor
id="markdown-editor"
ref={ref}
hideGutters
hideLineNumbers
dynamicHeight={!tall}
showPrettifyButton
noStyleActiveLine
enableNunjucks
mode={mode || 'text/x-markdown'}
placeholder={placeholder}
defaultValue={markdown}
onChange={handleChange}
/>
</div>
</ErrorBoundary>
</TabPanel>
<TabPanel className="w-full flex-1 overflow-y-auto m-2" id="preview">
<ErrorBoundary
errorClassName="tall wide vertically-align font-error pad text-center"
>
<MarkdownPreview markdown={markdown} />
</ErrorBoundary>
</TabPanel>
</Tabs>

);
});
MarkdownEditor.displayName = 'MarkdownEditor';
5 changes: 2 additions & 3 deletions packages/insomnia/src/ui/components/markdown-preview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import classnames from 'classnames';
import highlight from 'highlight.js/lib/common';
import React, { FC, useEffect, useLayoutEffect, useRef, useState } from 'react';

Expand All @@ -12,7 +11,7 @@ interface Props {
heading?: string;
}

export const MarkdownPreview: FC<Props> = ({ markdown, className, heading }) => {
export const MarkdownPreview: FC<Props> = ({ markdown, heading }) => {
const divRef = useRef<HTMLDivElement>(null);
const [compiled, setCompiled] = useState('');
const [error, setError] = useState('');
Expand Down Expand Up @@ -55,7 +54,7 @@ export const MarkdownPreview: FC<Props> = ({ markdown, className, heading }) =>
};

return (
<div ref={divRef} className={classnames('markdown-preview', className)}>
<div ref={divRef}>
{error ? <p className="notice error no-margin">Failed to render: {error}</p> : null}
<div className="selectable">
{heading ? <h1 className="markdown-preview__content-title">{heading}</h1> : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,16 @@ import { ListWorkspacesLoaderData } from '../../routes/project';
import { Modal, type ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
import { ModalHeader } from '../base/modal-header';
import { CodeEditorHandle } from '../codemirror/code-editor';
import { HelpTooltip } from '../help-tooltip';
import { MarkdownEditor } from '../markdown-editor';

export interface RequestGroupSettingsModalOptions {
requestGroup: RequestGroup;
}
interface State {
defaultPreviewMode: boolean;
activeWorkspaceIdToCopyTo: string;
}

export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps & {
requestGroup: RequestGroup;
}) => {
const modalRef = useRef<ModalHandle>(null);
const editorRef = useRef<CodeEditorHandle>(null);
const { organizationId, projectId, workspaceId } = useParams() as { organizationId: string; projectId: string; workspaceId: string };
const workspacesFetcher = useFetcher<ListWorkspacesLoaderData>();
useEffect(() => {
Expand All @@ -36,10 +30,7 @@ export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps &
}, [organizationId, projectId, workspacesFetcher]);
const projectLoaderData = workspacesFetcher?.data;
const workspacesForActiveProject = projectLoaderData?.files.map(w => w.workspace).filter(isNotNullOrUndefined).filter(w => w.scope !== 'mock-server') || [];
const [state, setState] = useState<State>({
activeWorkspaceIdToCopyTo: '',
defaultPreviewMode: !!requestGroup.description,
});
const [workspaceToCopyTo, setWorkspaceToCopyTo] = useState('');
const patchRequestGroup = useRequestGroupPatcher();
const requestFetcher = useFetcher();

Expand All @@ -56,25 +47,21 @@ export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps &
}, []);
const navigate = useNavigate();
const handleMoveToWorkspace = async () => {
invariant(state.activeWorkspaceIdToCopyTo, 'Workspace ID is required');
patchRequestGroup(requestGroup._id, { parentId: state.activeWorkspaceIdToCopyTo });
invariant(workspaceToCopyTo, 'Workspace ID is required');
patchRequestGroup(requestGroup._id, { parentId: workspaceToCopyTo });
modalRef.current?.hide();
navigate(`/organization/${organizationId}/project/${projectId}/workspace/${state.activeWorkspaceIdToCopyTo}/debug`);
navigate(`/organization/${organizationId}/project/${projectId}/workspace/${workspaceToCopyTo}/debug`);
};

const handleCopyToWorkspace = async () => {
invariant(state.activeWorkspaceIdToCopyTo, 'Workspace ID is required');
invariant(workspaceToCopyTo, 'Workspace ID is required');
duplicateRequestGroup({
_id: requestGroup._id,
name: requestGroup.name, // Because duplicate will add (Copy) suffix if name is not provided in patch
parentId: state.activeWorkspaceIdToCopyTo,
parentId: workspaceToCopyTo,
});
};

const {
defaultPreviewMode,
activeWorkspaceIdToCopyTo,
} = state;
return (
<OverlayContainer onClick={e => e.stopPropagation()}>
<Modal ref={modalRef} onHide={onHide}>
Expand All @@ -99,18 +86,6 @@ export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps &
/>
</label>
</div>
<MarkdownEditor
ref={editorRef}
className="margin-top"
defaultPreviewMode={defaultPreviewMode}
placeholder="Write a description"
defaultValue={requestGroup?.description || ''}
onChange={async (description: string) => {
invariant(requestGroup, 'No request group');
patchRequestGroup(requestGroup._id, { description });
}}
/>
<hr />
<div className="form-row">
<div className="form-control form-control--outlined">
<label>
Expand All @@ -120,13 +95,9 @@ export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps &
placed at the root of the new workspace's folder structure.
</HelpTooltip>
<select
value={activeWorkspaceIdToCopyTo}
value={workspaceToCopyTo}
onChange={event => {
const activeWorkspaceIdToCopyTo = event.currentTarget.value;
setState(state => ({
...state,
activeWorkspaceIdToCopyTo,
}));
setWorkspaceToCopyTo(event.currentTarget.value);
}}
>
<option value="">-- Select Workspace --</option>
Expand All @@ -143,7 +114,7 @@ export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps &
</div>
<div className="form-control form-control--no-label width-auto">
<button
disabled={!activeWorkspaceIdToCopyTo}
disabled={!workspaceToCopyTo}
className="btn btn--clicky"
onClick={handleCopyToWorkspace}
>
Expand All @@ -152,7 +123,7 @@ export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps &
</div>
<div className="form-control form-control--no-label width-auto">
<button
disabled={!activeWorkspaceIdToCopyTo}
disabled={!workspaceToCopyTo}
className="btn btn--clicky"
onClick={handleMoveToWorkspace}
>
Expand Down
Loading

0 comments on commit f364d71

Please sign in to comment.