mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-26 14:15:52 +00:00
fix: restore cursor focus on save and show placeholder for empty cells (#6795)
This commit is contained in:
@@ -56,7 +56,7 @@ const Headers = ({ collection }) => {
|
||||
isKeyField: true,
|
||||
placeholder: 'Name',
|
||||
width: '30%',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -64,7 +64,7 @@ const Headers = ({ collection }) => {
|
||||
onChange={(newValue) => onChange(newValue.replace(/[\r\n]/g, ''))}
|
||||
autocomplete={headerAutoCompleteList}
|
||||
collection={collection}
|
||||
placeholder={isLastEmptyRow ? 'Name' : ''}
|
||||
placeholder={!value ? 'Name' : ''}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@@ -72,7 +72,7 @@ const Headers = ({ collection }) => {
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
placeholder: 'Value',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -80,7 +80,7 @@ const Headers = ({ collection }) => {
|
||||
onChange={onChange}
|
||||
collection={collection}
|
||||
autocomplete={MimeTypes}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,14 +46,14 @@ const VarsTable = ({ collection, vars, varType }) => {
|
||||
</div>
|
||||
),
|
||||
placeholder: varType === 'request' ? 'Value' : 'Expr',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<MultiLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
onSave={onSave}
|
||||
onChange={onChange}
|
||||
collection={collection}
|
||||
placeholder={isLastEmptyRow ? (varType === 'request' ? 'Value' : 'Expr') : ''}
|
||||
placeholder={!value ? (varType === 'request' ? 'Value' : 'Expr') : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react';
|
||||
import { IconTrash, IconAlertCircle, IconGripVertical, IconMinusVertical } from '@tabler/icons';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { uuid } from 'utils/common';
|
||||
@@ -312,7 +312,7 @@ const EditableTable = ({
|
||||
className="mousetrap"
|
||||
value={value || ''}
|
||||
readOnly={column.readOnly}
|
||||
placeholder={isEmpty ? column.placeholder || column.name : ''}
|
||||
placeholder={!value ? column.placeholder || column.name : ''}
|
||||
onChange={(e) => handleValueChange(row.uid, column.key, e.target.value)}
|
||||
/>
|
||||
{errorIcon}
|
||||
|
||||
@@ -472,7 +472,7 @@ const EnvironmentVariablesTable = ({
|
||||
id={`${actualIndex}.name`}
|
||||
name={`${actualIndex}.name`}
|
||||
value={variable.name}
|
||||
placeholder={isLastEmptyRow ? 'Name' : ''}
|
||||
placeholder={!variable.value || (typeof variable.value === 'string' && variable.value.trim() === '') ? 'Value' : ''}
|
||||
onChange={(e) => handleNameChange(actualIndex, e)}
|
||||
onBlur={() => handleNameBlur(actualIndex)}
|
||||
onKeyDown={(e) => handleNameKeyDown(actualIndex, e)}
|
||||
|
||||
@@ -60,7 +60,7 @@ const Headers = ({ collection, folder }) => {
|
||||
isKeyField: true,
|
||||
placeholder: 'Name',
|
||||
width: '30%',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -68,7 +68,7 @@ const Headers = ({ collection, folder }) => {
|
||||
onChange={(newValue) => onChange(newValue.replace(/[\r\n]/g, ''))}
|
||||
autocomplete={headerAutoCompleteList}
|
||||
collection={collection}
|
||||
placeholder={isLastEmptyRow ? 'Name' : ''}
|
||||
placeholder={!value ? 'Name' : ''}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@@ -76,7 +76,7 @@ const Headers = ({ collection, folder }) => {
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
placeholder: 'Value',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -85,7 +85,7 @@ const Headers = ({ collection, folder }) => {
|
||||
collection={collection}
|
||||
item={folder}
|
||||
autocomplete={MimeTypes}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ const VarsTable = ({ folder, collection, vars, varType }) => {
|
||||
</div>
|
||||
),
|
||||
placeholder: varType === 'request' ? 'Value' : 'Expr',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<MultiLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -59,7 +59,7 @@ const VarsTable = ({ folder, collection, vars, varType }) => {
|
||||
onChange={onChange}
|
||||
collection={collection}
|
||||
item={folder}
|
||||
placeholder={isLastEmptyRow ? (varType === 'request' ? 'Value' : 'Expr') : ''}
|
||||
placeholder={!value ? (varType === 'request' ? 'Value' : 'Expr') : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ const Assertions = ({ item, collection }) => {
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
width: '30%',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => {
|
||||
render: ({ row, value, onChange }) => {
|
||||
const { operator, value: assertionValue } = parseAssertionOperator(value);
|
||||
|
||||
if (isUnaryOperator(operator)) {
|
||||
@@ -141,7 +141,7 @@ const Assertions = ({ item, collection }) => {
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ const FormUrlEncodedParams = ({ item, collection }) => {
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
placeholder: 'Value',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<MultiLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -57,7 +57,7 @@ const FormUrlEncodedParams = ({ item, collection }) => {
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ const MultipartFormParams = ({ item, collection }) => {
|
||||
allowNewlines={true}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
</div>
|
||||
{!hasTextValue && !isLastEmptyRow && (
|
||||
@@ -177,12 +177,12 @@ const MultipartFormParams = ({ item, collection }) => {
|
||||
key: 'contentType',
|
||||
name: 'Content-Type',
|
||||
placeholder: 'Auto',
|
||||
width: '30%',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
width: '20%',
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
onSave={onSave}
|
||||
theme={storedTheme}
|
||||
placeholder={isLastEmptyRow ? 'Auto' : ''}
|
||||
placeholder={!value ? 'Auto' : ''}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
onRun={handleRun}
|
||||
|
||||
@@ -70,7 +70,7 @@ const QueryParams = ({ item, collection }) => {
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
placeholder: 'Value',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<MultiLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -80,7 +80,7 @@ const QueryParams = ({ item, collection }) => {
|
||||
collection={collection}
|
||||
item={item}
|
||||
variablesAutocomplete={true}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ const RequestHeaders = ({ item, collection, addHeaderText }) => {
|
||||
isKeyField: true,
|
||||
placeholder: 'Name',
|
||||
width: '30%',
|
||||
render: ({ value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -76,7 +76,7 @@ const RequestHeaders = ({ item, collection, addHeaderText }) => {
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? 'Name' : ''}
|
||||
placeholder={!value ? 'Name' : ''}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@@ -84,7 +84,7 @@ const RequestHeaders = ({ item, collection, addHeaderText }) => {
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
placeholder: 'Value',
|
||||
render: ({ value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -94,7 +94,7 @@ const RequestHeaders = ({ item, collection, addHeaderText }) => {
|
||||
autocomplete={MimeTypes}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ const VarsTable = ({ item, collection, vars, varType }) => {
|
||||
</div>
|
||||
),
|
||||
placeholder: varType === 'request' ? 'Value' : 'Expr',
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<MultiLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -70,7 +70,7 @@ const VarsTable = ({ item, collection, vars, varType }) => {
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? (varType === 'request' ? 'Value' : 'Expr') : ''}
|
||||
placeholder={!value ? (varType === 'request' ? 'Value' : 'Expr') : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -130,12 +130,12 @@ const ResponseExampleFileBody = ({ item, collection, exampleUid, editMode = fals
|
||||
placeholder: 'Auto',
|
||||
width: '30%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
className="flex items-center justify-center"
|
||||
onSave={() => {}}
|
||||
theme={storedTheme}
|
||||
placeholder={isLastEmptyRow ? 'Auto' : ''}
|
||||
placeholder={!value ? 'Auto' : ''}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
onRun={() => {}}
|
||||
|
||||
@@ -58,7 +58,7 @@ const ResponseExampleFormUrlEncodedParams = ({ item, collection, exampleUid, edi
|
||||
placeholder: 'Value',
|
||||
width: '60%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<MultiLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -68,7 +68,7 @@ const ResponseExampleFormUrlEncodedParams = ({ item, collection, exampleUid, edi
|
||||
onRun={() => {}}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ const ResponseExampleHeaders = ({ editMode, item, collection, exampleUid }) => {
|
||||
placeholder: 'Key',
|
||||
width: '40%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
readOnly={!editMode}
|
||||
@@ -78,7 +78,7 @@ const ResponseExampleHeaders = ({ editMode, item, collection, exampleUid }) => {
|
||||
autocomplete={headerAutoCompleteList}
|
||||
onRun={() => {}}
|
||||
collection={collection}
|
||||
placeholder={isLastEmptyRow ? 'Key' : ''}
|
||||
placeholder={!value ? 'Key' : ''}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@@ -88,7 +88,7 @@ const ResponseExampleHeaders = ({ editMode, item, collection, exampleUid }) => {
|
||||
placeholder: 'Value',
|
||||
width: '60%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
readOnly={!editMode}
|
||||
@@ -100,7 +100,7 @@ const ResponseExampleHeaders = ({ editMode, item, collection, exampleUid }) => {
|
||||
allowNewlines={true}
|
||||
collection={collection}
|
||||
item={item}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ const ResponseExampleMultipartFormParams = ({ item, collection, exampleUid, edit
|
||||
collection={collection}
|
||||
item={item}
|
||||
readOnly={!editMode}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
</div>
|
||||
{!hasTextValue && !isLastEmptyRow && (
|
||||
@@ -228,11 +228,11 @@ const ResponseExampleMultipartFormParams = ({ item, collection, exampleUid, edit
|
||||
placeholder: 'Auto',
|
||||
width: '30%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
onSave={() => {}}
|
||||
theme={storedTheme}
|
||||
placeholder={isLastEmptyRow ? 'Auto' : ''}
|
||||
placeholder={!value ? 'Auto' : ''}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
onRun={() => {}}
|
||||
|
||||
@@ -105,7 +105,7 @@ const ResponseExampleParams = ({ editMode, item, collection, exampleUid }) => {
|
||||
placeholder: 'Name',
|
||||
width: '40%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -115,7 +115,7 @@ const ResponseExampleParams = ({ editMode, item, collection, exampleUid }) => {
|
||||
collection={collection}
|
||||
variablesAutocomplete={true}
|
||||
readOnly={!editMode}
|
||||
placeholder={isLastEmptyRow ? 'Name' : ''}
|
||||
placeholder={!value ? 'Name' : ''}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@@ -125,7 +125,7 @@ const ResponseExampleParams = ({ editMode, item, collection, exampleUid }) => {
|
||||
placeholder: 'Value',
|
||||
width: '60%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -135,7 +135,7 @@ const ResponseExampleParams = ({ editMode, item, collection, exampleUid }) => {
|
||||
collection={collection}
|
||||
variablesAutocomplete={true}
|
||||
readOnly={!editMode}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -154,7 +154,7 @@ const ResponseExampleParams = ({ editMode, item, collection, exampleUid }) => {
|
||||
placeholder: 'Value',
|
||||
width: '60%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -164,7 +164,7 @@ const ResponseExampleParams = ({ editMode, item, collection, exampleUid }) => {
|
||||
collection={collection}
|
||||
variablesAutocomplete={true}
|
||||
readOnly={!editMode}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ const ResponseExampleResponseHeaders = ({ editMode, item, collection, exampleUid
|
||||
placeholder: 'Key',
|
||||
width: '40%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -134,7 +134,7 @@ const ResponseExampleResponseHeaders = ({ editMode, item, collection, exampleUid
|
||||
onRun={() => {}}
|
||||
collection={collection}
|
||||
readOnly={!editMode}
|
||||
placeholder={isLastEmptyRow ? 'Key' : ''}
|
||||
placeholder={!value ? 'Key' : ''}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@@ -144,7 +144,7 @@ const ResponseExampleResponseHeaders = ({ editMode, item, collection, exampleUid
|
||||
placeholder: 'Value',
|
||||
width: '60%',
|
||||
readOnly: !editMode,
|
||||
render: ({ row, value, onChange, isLastEmptyRow }) => (
|
||||
render: ({ value, onChange }) => (
|
||||
<SingleLineEditor
|
||||
value={value || ''}
|
||||
theme={storedTheme}
|
||||
@@ -156,7 +156,7 @@ const ResponseExampleResponseHeaders = ({ editMode, item, collection, exampleUid
|
||||
collection={collection}
|
||||
item={item}
|
||||
readOnly={!editMode}
|
||||
placeholder={isLastEmptyRow ? 'Value' : ''}
|
||||
placeholder={!value ? 'Value' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,51 @@ const wsStatusCodes = {
|
||||
1015: 'TLS_HANDSHAKE'
|
||||
};
|
||||
|
||||
/**
|
||||
* Preserves UIDs from existing array items when merging with new data.
|
||||
* UIDs are matched by position to keep React keys stable after file reloads.
|
||||
*/
|
||||
const preserveUidsAtPaths = (existing, updated, paths) => {
|
||||
if (!existing || !updated) return updated;
|
||||
|
||||
const merged = cloneDeep(updated);
|
||||
|
||||
paths.forEach((path) => {
|
||||
const newArray = get(merged, path);
|
||||
const existingArray = get(existing, path, []);
|
||||
|
||||
if (Array.isArray(newArray) && newArray.length) {
|
||||
set(
|
||||
merged,
|
||||
path,
|
||||
newArray.map((item, i) => (existingArray[i]?.uid ? { ...item, uid: existingArray[i].uid } : item))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return merged;
|
||||
};
|
||||
|
||||
// Paths containing arrays with UIDs that need preservation
|
||||
const REQUEST_UID_PATHS = [
|
||||
'params',
|
||||
'headers',
|
||||
'vars.req',
|
||||
'vars.res',
|
||||
'assertions',
|
||||
'body.formUrlEncoded',
|
||||
'body.multipartForm',
|
||||
'body.file'
|
||||
];
|
||||
|
||||
const ROOT_UID_PATHS = ['request.headers', 'request.vars.req', 'request.vars.res'];
|
||||
|
||||
const mergeRequestWithPreservedUids = (existingRequest, newRequest) =>
|
||||
preserveUidsAtPaths(existingRequest, newRequest, REQUEST_UID_PATHS);
|
||||
|
||||
const mergeRootWithPreservedUids = (existingRoot, newRoot) =>
|
||||
preserveUidsAtPaths(existingRoot, newRoot, ROOT_UID_PATHS);
|
||||
|
||||
const initialState = {
|
||||
collections: [],
|
||||
collectionSortOrder: 'default',
|
||||
@@ -2561,7 +2606,7 @@ export const collectionsSlice = createSlice({
|
||||
const collection = findCollectionByUid(state.collections, file.meta.collectionUid);
|
||||
if (isCollectionRoot) {
|
||||
if (collection) {
|
||||
collection.root = file.data;
|
||||
collection.root = mergeRootWithPreservedUids(collection.root, file.data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2573,7 +2618,7 @@ export const collectionsSlice = createSlice({
|
||||
if (file?.data?.meta?.name) {
|
||||
folderItem.name = file?.data?.meta?.name;
|
||||
}
|
||||
folderItem.root = file.data;
|
||||
folderItem.root = mergeRootWithPreservedUids(folderItem.root, file.data);
|
||||
if (file?.data?.meta?.seq) {
|
||||
folderItem.seq = file.data?.meta?.seq;
|
||||
}
|
||||
@@ -2621,7 +2666,7 @@ export const collectionsSlice = createSlice({
|
||||
currentItem.type = file.data.type;
|
||||
currentItem.seq = file.data.seq;
|
||||
currentItem.tags = file.data.tags;
|
||||
currentItem.request = file.data.request;
|
||||
currentItem.request = mergeRequestWithPreservedUids(currentItem.request, file.data.request);
|
||||
currentItem.filename = file.meta.name;
|
||||
currentItem.pathname = file.meta.pathname;
|
||||
currentItem.settings = file.data.settings;
|
||||
@@ -2700,7 +2745,7 @@ export const collectionsSlice = createSlice({
|
||||
const collection = findCollectionByUid(state.collections, file.meta.collectionUid);
|
||||
if (isCollectionRoot) {
|
||||
if (collection) {
|
||||
collection.root = file.data;
|
||||
collection.root = mergeRootWithPreservedUids(collection.root, file.data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2715,7 +2760,7 @@ export const collectionsSlice = createSlice({
|
||||
if (file?.data?.meta?.seq) {
|
||||
folderItem.seq = file?.data?.meta?.seq;
|
||||
}
|
||||
folderItem.root = file.data;
|
||||
folderItem.root = mergeRootWithPreservedUids(folderItem.root, file.data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2740,7 +2785,7 @@ export const collectionsSlice = createSlice({
|
||||
item.type = file.data.type;
|
||||
item.seq = file.data.seq;
|
||||
item.tags = file.data.tags;
|
||||
item.request = file.data.request;
|
||||
item.request = mergeRequestWithPreservedUids(item.request, file.data.request);
|
||||
item.settings = file.data.settings;
|
||||
item.examples = file.data.examples;
|
||||
item.filename = file.meta.name;
|
||||
|
||||
49
tests/editable-table/editable-table.spec.ts
Normal file
49
tests/editable-table/editable-table.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { createCollection, closeAllCollections, createRequest, selectRequestPaneTab } from '../utils/page';
|
||||
|
||||
test.describe('EditableTable - Focus and Placeholder', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Cursor focus restored after save and placeholder shown for empty value', async ({ page, createTmpDir }) => {
|
||||
const collectionName = 'test-editable-table';
|
||||
|
||||
// Create a new collection
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
|
||||
// Create a request
|
||||
await createRequest(page, 'Test Request', collectionName, {
|
||||
url: 'https://httpbin.org/get'
|
||||
});
|
||||
|
||||
// Navigate to Params tab
|
||||
await selectRequestPaneTab(page, 'Params');
|
||||
|
||||
// Find the Query params table
|
||||
const queryTable = page.locator('table').first();
|
||||
const firstRow = queryTable.locator('tbody tr').first();
|
||||
|
||||
// Get the Name input (regular input)
|
||||
const nameInput = firstRow.locator('input[type="text"]').first();
|
||||
await nameInput.click();
|
||||
await page.keyboard.type('testParam');
|
||||
|
||||
// Verify input has focus before save
|
||||
await expect(nameInput).toBeFocused();
|
||||
|
||||
// Save the request
|
||||
await page.keyboard.press('Meta+s');
|
||||
|
||||
// Wait for save toast
|
||||
await expect(page.getByText('Request saved successfully').last()).toBeVisible();
|
||||
|
||||
// Verify cursor focus is restored after save
|
||||
await expect(nameInput).toBeFocused();
|
||||
|
||||
// Verify placeholder shows for empty Value field
|
||||
const valueCell = firstRow.locator('[data-testid="column-value"]');
|
||||
const placeholder = valueCell.locator('pre.CodeMirror-placeholder');
|
||||
await expect(placeholder).toHaveText('Value');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user