move env editor to folder tab (#7534)

* move env editor to folder tab

* remove old unused test
This commit is contained in:
Jack Kavanagh 2024-06-12 10:05:42 +02:00 committed by GitHub
parent 9db2d42feb
commit 8648f49d4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 42 additions and 175 deletions

View File

@ -144,80 +144,5 @@ test.describe('Debug-Sidebar', async () => {
await page.getByRole('menuitemradio', { name: 'Http Request' }).click(); await page.getByRole('menuitemradio', { name: 'Http Request' }).click();
await page.getByLabel('Request Collection').getByRole('row', { name: 'New Request' }).click(); await page.getByLabel('Request Collection').getByRole('row', { name: 'New Request' }).click();
}); });
test('Add new string variable via environment overrides', async ({ page }) => {
// Create new Folder
await page.getByLabel('Create in collection').click();
await page.getByLabel('New Folder').click();
await page.locator('#prompt-input').fill('New Folder');
await page.getByText('New Folder').press('Enter');
// Open 'New folder' folder
const folderLocator = page.getByTestId('Dropdown-New-Folder');
const environmentLocator = page.getByRole('menuitemradio', { name: 'Environment' });
await folderLocator.click();
await environmentLocator.click();
// Add a new string environment variable
const expected = '{ "foo":"bar" }';
const editorTextLocator = await page.getByTestId('CodeEditor').getByRole('textbox');
const selectAllShortcut = process.platform === 'darwin' ? 'Meta+A' : 'Control+A';
await editorTextLocator.press(selectAllShortcut);
await editorTextLocator.fill(expected);
// Close and re-open modal
await page.getByText('Close').click();
await folderLocator.click();
await environmentLocator.click();
// Validate expected values persisted
const actualRows = await page.getByTestId('CodeEditor').locator('.CodeMirror-line').allInnerTexts();
expect(actualRows.length).toBeGreaterThan(0);
const actualJSON = JSON.parse(actualRows.join(' '));
expect(actualJSON).toEqual(JSON.parse(expected));
});
test('Add new string variable to an existing environment overrides folder', async ({ page }) => {
// Open 'Test Folder' folder
const folderLocator = page.getByTestId('Dropdown-test-folder');
const environmentLocator = page.getByRole('menuitemradio', { name: 'Environment' });
await folderLocator.click();
await environmentLocator.click();
// Add a new string environment variable to existing overrides
// 1. Retrieve current editor rows
const editorLocator = page.getByTestId('CodeEditor').locator('.CodeMirror-line');
const rows = await editorLocator.allInnerTexts();
// 2. Merge rows and convert to JSON
const editorJSON = JSON.parse(rows.join(' '));
// 3. Modify JSON with new string environment variable
editorJSON.REQUEST = 'HTTP';
const expected = editorJSON;
// 4. Apply new JSON to editor
const editorTextLocator = await page.getByTestId('CodeEditor').getByRole('textbox');
const selectAllShortcut = process.platform === 'darwin' ? 'Meta+A' : 'Control+A';
await editorTextLocator.press(selectAllShortcut);
await editorTextLocator.fill(JSON.stringify(expected));
// Close and re-open Modal
await page.getByText('Close').click();
await folderLocator.click();
await environmentLocator.click();
// Validate expected values persisted
const actualRows = await editorLocator.allInnerTexts();
expect(actualRows.length).toBeGreaterThan(0);
const actualJSON = JSON.parse(actualRows.join(' '));
expect(actualJSON).toEqual(expected);
});
// TODO: more scenarios will be added in follow-up iterations of increasing test coverage
}); });
}); });

View File

@ -91,7 +91,7 @@ export interface CodeEditorProps {
noLint?: boolean; noLint?: boolean;
noMatchBrackets?: boolean; noMatchBrackets?: boolean;
noStyleActiveLine?: boolean; noStyleActiveLine?: boolean;
// used only for saving env editor state // used only for saving env editor state, focusEvent doesn't work well
onBlur?: (e: FocusEvent) => void; onBlur?: (e: FocusEvent) => void;
onChange?: (value: string) => void; onChange?: (value: string) => void;
onPaste?: (value: string) => string; onPaste?: (value: string) => string;

View File

@ -19,7 +19,6 @@ import { type DropdownHandle, type DropdownProps } from '../base/dropdown';
import { Icon } from '../icon'; import { Icon } from '../icon';
import { showError, showModal, showPrompt } from '../modals'; import { showError, showModal, showPrompt } from '../modals';
import { AskModal } from '../modals/ask-modal'; import { AskModal } from '../modals/ask-modal';
import { EnvironmentEditModal } from '../modals/environment-edit-modal';
import { PasteCurlModal } from '../modals/paste-curl-modal'; import { PasteCurlModal } from '../modals/paste-curl-modal';
import { RequestGroupSettingsModal } from '../modals/request-group-settings-modal'; import { RequestGroupSettingsModal } from '../modals/request-group-settings-modal';
interface Props extends Partial<DropdownProps> { interface Props extends Partial<DropdownProps> {
@ -226,12 +225,6 @@ export const RequestGroupActionsDropdown = ({
hint: hotKeyRegistry.request_createHTTP, hint: hotKeyRegistry.request_createHTTP,
action: () => handleRequestGroupDuplicate(), action: () => handleRequestGroupDuplicate(),
}, },
{
id: 'Environment',
name: 'Environment',
icon: 'code',
action: () => showModal(EnvironmentEditModal, { requestGroup }),
},
{ {
id: 'Rename', id: 'Rename',
name: 'Rename', name: 'Rename',

View File

@ -1,86 +0,0 @@
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
import { RequestGroup } from '../../../models/request-group';
import { useRequestGroupPatcher } from '../../hooks/use-request';
import { Modal, type ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
import { ModalFooter } from '../base/modal-footer';
import { ModalHeader } from '../base/modal-header';
import { EnvironmentEditor, EnvironmentEditorHandle } from '../editors/environment-editor';
interface State {
requestGroup: RequestGroup | null;
}
export interface EnvironmentEditModalOptions {
requestGroup: RequestGroup;
}
export interface EnvironmentEditModalHandle {
show: (options: EnvironmentEditModalOptions) => void;
hide: () => void;
}
export const EnvironmentEditModal = forwardRef<EnvironmentEditModalHandle, ModalProps>((props, ref) => {
const modalRef = useRef<ModalHandle>(null);
const environmentEditorRef = useRef<EnvironmentEditorHandle>(null);
const [state, setState] = useState<State>({
requestGroup: null,
});
useImperativeHandle(ref, () => ({
hide: () => {
modalRef.current?.hide();
},
show: ({ requestGroup }) => {
setState(state => ({ ...state, requestGroup }));
modalRef.current?.show();
},
}), []);
const patchGroup = useRequestGroupPatcher();
const { requestGroup } = state;
const environmentInfo = {
object: requestGroup ? requestGroup.environment : {},
propertyOrder: requestGroup && requestGroup.environmentPropertyOrder,
};
const saveChanges = () => {
setState({ requestGroup });
if (environmentEditorRef.current?.isValid()) {
try {
const data = environmentEditorRef.current?.getValue();
if (state.requestGroup && data) {
patchGroup(state.requestGroup._id, {
environment: data.object,
environmentPropertyOrder: data.propertyOrder,
});
}
} catch (err) {
console.warn('Failed to update environment', err);
}
}
};
return (
<Modal ref={modalRef} tall {...props} onHide={saveChanges}>
<ModalHeader>Environment Overrides (JSON Format)</ModalHeader>
<ModalBody noScroll className="pad-top-sm">
<EnvironmentEditor
ref={environmentEditorRef}
key={requestGroup ? requestGroup._id : 'n/a'}
environmentInfo={environmentInfo}
onBlur={saveChanges}
/>
</ModalBody>
<ModalFooter>
<div className="margin-left italic txt-sm">
* Used to override data in the global environment
</div>
<button className="btn" onClick={() => modalRef.current?.hide()}>
Close
</button>
</ModalFooter>
</Modal >
);
});
EnvironmentEditModal.displayName = 'EnvironmentEditModal';

View File

@ -1,4 +1,4 @@
import React, { FC, useState } from 'react'; import React, { FC, useRef, useState } from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components'; import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
import { useRouteLoaderData } from 'react-router-dom'; import { useRouteLoaderData } from 'react-router-dom';
@ -8,6 +8,7 @@ import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../hooks/us
import { RequestGroupLoaderData } from '../../routes/request-group'; import { RequestGroupLoaderData } from '../../routes/request-group';
import { WorkspaceLoaderData } from '../../routes/workspace'; import { WorkspaceLoaderData } from '../../routes/workspace';
import { AuthWrapper } from '../editors/auth/auth-wrapper'; import { AuthWrapper } from '../editors/auth/auth-wrapper';
import { EnvironmentEditor, EnvironmentEditorHandle } from '../editors/environment-editor';
import { RequestHeadersEditor } from '../editors/request-headers-editor'; import { RequestHeadersEditor } from '../editors/request-headers-editor';
import { RequestScriptEditor } from '../editors/request-script-editor'; import { RequestScriptEditor } from '../editors/request-script-editor';
import { ErrorBoundary } from '../error-boundary'; import { ErrorBoundary } from '../error-boundary';
@ -25,7 +26,24 @@ export const RequestGroupPane: FC<{ settings: Settings }> = ({ settings }) => {
const uniqueKey = `${activeEnvironment?.modified}::${activeRequestGroup._id}::${gitVersion}::${activeRequestSyncVersion}`; const uniqueKey = `${activeEnvironment?.modified}::${activeRequestGroup._id}::${gitVersion}::${activeRequestSyncVersion}`;
const folderHeaders = activeRequestGroup?.headers || []; const folderHeaders = activeRequestGroup?.headers || [];
const headersCount = folderHeaders.filter(h => !h.disabled)?.length || 0; const headersCount = folderHeaders.filter(h => !h.disabled)?.length || 0;
const environmentEditorRef = useRef<EnvironmentEditorHandle>(null);
const patchGroup = useRequestGroupPatcher();
const saveChanges = () => {
if (environmentEditorRef.current?.isValid()) {
try {
const data = environmentEditorRef.current?.getValue();
if (activeRequestGroup && data) {
patchGroup(activeRequestGroup._id, {
environment: data.object,
environmentPropertyOrder: data.propertyOrder,
});
}
} catch (err) {
console.warn('Failed to update environment', err);
}
}
};
return ( return (
<> <>
<Tabs aria-label='Request group tabs' className="flex-1 w-full h-full flex flex-col"> <Tabs aria-label='Request group tabs' className="flex-1 w-full h-full flex flex-col">
@ -58,6 +76,12 @@ export const RequestGroupPane: FC<{ settings: Settings }> = ({ settings }) => {
</span> </span>
)} )}
</Tab> </Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='environment'
>
Environment
</Tab>
<Tab <Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300' className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='docs' id='docs'
@ -145,6 +169,22 @@ export const RequestGroupPane: FC<{ settings: Settings }> = ({ settings }) => {
</TabPanel> </TabPanel>
</Tabs> </Tabs>
</TabPanel> </TabPanel>
<TabPanel className='w-full flex-1 overflow-y-auto ' id='environment'>
<ErrorBoundary
key={uniqueKey}
errorClassName="font-error pad text-center"
>
<EnvironmentEditor
ref={environmentEditorRef}
key={activeRequestGroup ? activeRequestGroup._id : 'n/a'}
environmentInfo={{
object: activeRequestGroup ? activeRequestGroup.environment : {},
propertyOrder: activeRequestGroup && activeRequestGroup.environmentPropertyOrder,
}}
onBlur={saveChanges}
/>
</ErrorBoundary>
</TabPanel>
<TabPanel className='w-full flex-1 overflow-y-auto ' id='docs'> <TabPanel className='w-full flex-1 overflow-y-auto ' id='docs'>
{activeRequestGroup.description ? ( {activeRequestGroup.description ? (
<div> <div>

View File

@ -7,7 +7,6 @@ import { AddKeyCombinationModal } from '../components/modals/add-key-combination
import { AlertModal } from '../components/modals/alert-modal'; import { AlertModal } from '../components/modals/alert-modal';
import { AskModal } from '../components/modals/ask-modal'; import { AskModal } from '../components/modals/ask-modal';
import { CodePromptModal } from '../components/modals/code-prompt-modal'; import { CodePromptModal } from '../components/modals/code-prompt-modal';
import { EnvironmentEditModal } from '../components/modals/environment-edit-modal';
import { ErrorModal } from '../components/modals/error-modal'; import { ErrorModal } from '../components/modals/error-modal';
import { FilterHelpModal } from '../components/modals/filter-help-modal'; import { FilterHelpModal } from '../components/modals/filter-help-modal';
import { GenerateCodeModal } from '../components/modals/generate-code-modal'; import { GenerateCodeModal } from '../components/modals/generate-code-modal';
@ -72,10 +71,6 @@ const Modals: FC = () => {
ref={instance => registerModal(instance, 'ResponseDebugModal')} ref={instance => registerModal(instance, 'ResponseDebugModal')}
/> />
<EnvironmentEditModal
ref={instance => registerModal(instance, 'EnvironmentEditModal')}
/>
<AddKeyCombinationModal <AddKeyCombinationModal
ref={instance => registerModal(instance, 'AddKeyCombinationModal')} ref={instance => registerModal(instance, 'AddKeyCombinationModal')}
/> />