Skip to content

Commit 2d15944

Browse files
committed
Group nodes by tag in AddNodes panel to improve discoverability
1 parent cd168d2 commit 2d15944

File tree

6 files changed

+78
-33
lines changed

6 files changed

+78
-33
lines changed

packages/flowtest-electron/src/utils/collection.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const parseOpenAPISpec = (collection) => {
2525
Object.entries(operation).map(([requestType, request], _) => {
2626
const summary = request['summary'];
2727
const operationId = request['operationId'];
28+
const tags = request['tags'];
2829
var url = replaceSingleToDoubleCurlyBraces(computeUrl(baseUrl, path));
2930
var variables = {};
3031

@@ -59,6 +60,7 @@ const parseOpenAPISpec = (collection) => {
5960
description: summary,
6061
operationId: operationId,
6162
requestType: requestType.toUpperCase(),
63+
tags: tags,
6264
};
6365
// console.log(finalNode);
6466
parsedNodes.push(finalNode);

src/components/molecules/flow/AddNodes.js

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Fragment } from 'react';
66
import { Disclosure } from '@headlessui/react';
77
import { ChevronUpIcon } from '@heroicons/react/20/solid';
88
import useCollectionStore from 'stores/CollectionStore';
9+
import { orderNodesByTags } from './utils';
910

1011
// ToDo: Move these constants to constants file/folder
1112
const requestNodes = [
@@ -63,6 +64,7 @@ const AddNodes = ({ collectionId }) => {
6364

6465
// Get all requests of this collections
6566
const collection = useCollectionStore.getState().collections.find((c) => c.id === collectionId);
67+
const nodesByTags = orderNodesByTags(collection.nodes);
6668

6769
return (
6870
<>
@@ -129,23 +131,41 @@ const AddNodes = ({ collectionId }) => {
129131
</Disclosure.Button>
130132
<Disclosure.Panel className='px-4 pt-4 pb-2 text-sm border-l border-r'>
131133
<div>
132-
{collection.nodes.map((node) => (
133-
<div
134-
key={`${node.requestType} - ${node.operationId}`}
135-
onDragStart={(event) => {
136-
const newNode = {
137-
...node,
138-
type: 'requestNode',
139-
};
140-
onDragStart(event, newNode);
141-
}}
142-
draggable
143-
cursor='move'
144-
className='py-2 border-b'
145-
>
146-
<div className='text-base font-semibold primary-text'>{`${node.requestType} - ${node.operationId}`}</div>
147-
<div className='text-xs secondary-text'>{node.description}</div>
148-
</div>
134+
{Object.entries(nodesByTags).map(([tag, nodes], index) => (
135+
<Disclosure as='div' key={index}>
136+
{({ open }) => (
137+
<>
138+
<Disclosure.Button className='flex justify-between w-full px-4 py-2 text-lg font-medium text-left border-t border-b bg-gray-50 hover:bg-gray-100 focus:outline-none focus-visible:ring'>
139+
<span>{tag}</span>
140+
<ChevronUpIcon
141+
className={`${open ? 'rotate-180 transform' : ''} h-5 w-5 `}
142+
/>
143+
</Disclosure.Button>
144+
<Disclosure.Panel className='px-4 pt-4 pb-2 text-sm border-l border-r'>
145+
<div>
146+
{nodes.map((node) => (
147+
<div
148+
key={`${node.requestType} - ${node.operationId}`}
149+
onDragStart={(event) => {
150+
const newNode = {
151+
...node,
152+
type: 'requestNode',
153+
};
154+
onDragStart(event, newNode);
155+
}}
156+
draggable
157+
cursor='move'
158+
className='py-2 border-b'
159+
>
160+
<div className='text-base font-semibold primary-text'>{`${node.requestType} - ${node.operationId}`}</div>
161+
<div className='text-xs secondary-text'>{node.description}</div>
162+
</div>
163+
))}
164+
</div>
165+
</Disclosure.Panel>
166+
</>
167+
)}
168+
</Disclosure>
149169
))}
150170
</div>
151171
</Disclosure.Panel>

src/components/molecules/flow/nodes/RequestBody.js

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ const RequestBody = ({ nodeId, nodeData }) => {
4848
};
4949

5050
const handleClose = (option) => {
51-
if (option == 'raw-json') {
51+
if (option == 'None') {
52+
setRequestNodeBody(nodeId, option, undefined);
53+
} else if (option == 'raw-json') {
5254
setRequestNodeBody(nodeId, option, '');
5355
} else if (option == 'form-data') {
5456
setRequestNodeBody(nodeId, option, {
@@ -61,12 +63,12 @@ const RequestBody = ({ nodeId, nodeData }) => {
6163

6264
return (
6365
<>
64-
<div className='border-t border-neutral-300 bg-slate-100 px-2 py-4'>
66+
<div className='px-2 py-4 border-t border-neutral-300 bg-slate-100'>
6567
<div className='flex items-center justify-between font-medium'>
6668
<h3>Body</h3>
6769
<Menu as='div' className='relative inline-block text-left'>
6870
<Menu.Button data-click-from='body-type-menu' className='p-2'>
69-
<EllipsisVerticalIcon className='h-4 w-4' aria-hidden='true' data-click-from='body-type-menu' />
71+
<EllipsisVerticalIcon className='w-4 h-4' aria-hidden='true' data-click-from='body-type-menu' />
7072
</Menu.Button>
7173
<Transition
7274
as={Fragment}
@@ -78,13 +80,13 @@ const RequestBody = ({ nodeId, nodeData }) => {
7880
leaveTo='transform opacity-0 scale-95'
7981
>
8082
<Menu.Items
81-
className='absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white px-1 py-1 shadow-lg ring-1 ring-black/5 focus:outline-none'
83+
className='absolute right-0 z-10 w-56 px-1 py-1 mt-2 origin-top-right bg-white divide-y divide-gray-100 rounded-md shadow-lg ring-1 ring-black/5 focus:outline-none'
8284
data-click-from='body-type-menu'
8385
>
8486
{requestBodyTypeOptions.map((bodyTypeOption, index) => (
8587
<Menu.Item key={index} data-click-from='body-type-menu' onClick={() => handleClose(bodyTypeOption)}>
8688
<button
87-
className='group flex w-full items-center rounded-md px-2 py-2 text-sm text-gray-900 hover:bg-slate-100'
89+
className='flex items-center w-full px-2 py-2 text-sm text-gray-900 rounded-md group hover:bg-slate-100'
8890
data-click-from='body-type-menu'
8991
>
9092
{bodyTypeOption}
@@ -97,10 +99,10 @@ const RequestBody = ({ nodeId, nodeData }) => {
9799
</div>
98100
</div>
99101
{nodeData.requestBody && nodeData.requestBody.type === 'raw-json' && (
100-
<div className='border border-t border-neutral-300 bg-slate-50 p-2'>
102+
<div className='p-2 border border-t border-neutral-300 bg-slate-50'>
101103
<textarea
102104
placeholder='Enter json'
103-
className='nodrag nowheel w-full p-2'
105+
className='w-full p-2 nodrag nowheel'
104106
name='username'
105107
onChange={(e) => handleRawJson(e)}
106108
rows={4}
@@ -109,25 +111,25 @@ const RequestBody = ({ nodeId, nodeData }) => {
109111
</div>
110112
)}
111113
{nodeData.requestBody && nodeData.requestBody.type === 'form-data' && (
112-
<div className='border-t border-neutral-300 bg-slate-50 p-2'>
113-
<div className='flex items-center justify-between rounded-md border border-neutral-500 text-sm text-neutral-500 outline-0 focus:ring-0'>
114+
<div className='p-2 border-t border-neutral-300 bg-slate-50'>
115+
<div className='flex items-center justify-between text-sm border rounded-md border-neutral-500 text-neutral-500 outline-0 focus:ring-0'>
114116
<input
115117
placeholder='key'
116-
className='nodrag nowheel bg-slate-50 pl-4'
118+
className='pl-4 nodrag nowheel bg-slate-50'
117119
name='variable-value'
118120
onChange={(e) => handleFormDataKey(e)}
119121
value={nodeData.requestBody.body.key}
120122
/>
121-
<div className='rounded-br-md rounded-tr-md border-l border-l-neutral-500 px-4 py-2'>File</div>
123+
<div className='px-4 py-2 border-l rounded-br-md rounded-tr-md border-l-neutral-500'>File</div>
122124
</div>
123125
<div className='py-2'>
124126
<button
125-
className='flex w-full cursor-pointer items-center justify-center gap-2 rounded-md border border-neutral-500 bg-slate-100 p-2 hover:bg-slate-200'
127+
className='flex items-center justify-center w-full gap-2 p-2 border rounded-md cursor-pointer border-neutral-500 bg-slate-100 hover:bg-slate-200'
126128
onClick={() => {
127129
uploadFileForRequestNode.current.click();
128130
}}
129131
>
130-
<DocumentArrowUpIcon className='h-4 w-4 text-center' />
132+
<DocumentArrowUpIcon className='w-4 h-4 text-center' />
131133
Upload File
132134
{/* Ref: https://stackoverflow.com/questions/37457128/react-open-file-browser-on-click-a-div */}
133135
<div className='hidden'>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export const orderNodesByTags = (nodes) => {
2+
const result = {};
3+
if (nodes) {
4+
nodes.map((node) => {
5+
node.tags.map((tag) => {
6+
if (!result[tag]) {
7+
result[tag] = [];
8+
}
9+
result[tag].push(node);
10+
});
11+
});
12+
}
13+
return result;
14+
};

src/components/molecules/headers/TabPanelHeader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ const TabPanelHeader = () => {
9494
</div>
9595
</>
9696
) : (
97-
<div className='text-base tracking-[0.15em]'> Please select a flow test from the sidebar </div>
97+
<div className='text-base tracking-[0.15em]'> Please select a flow from the sidebar </div>
9898
)}
9999
</div>
100100
);

src/stores/CanvasStore.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,22 @@ const useCanvasStore = create((set, get) => ({
163163
nodes: get().nodes.map((node) => {
164164
if (node.id === nodeId) {
165165
// it's important to create a new object here, to inform React Flow about the cahnges
166-
if (type == 'raw-json') {
166+
if (type === 'None') {
167+
node.data = {
168+
...node.data,
169+
requestBody: {
170+
type,
171+
},
172+
};
173+
} else if (type === 'raw-json') {
167174
node.data = {
168175
...node.data,
169176
requestBody: {
170177
type,
171178
body: data,
172179
},
173180
};
174-
} else if (type == 'form-data') {
181+
} else if (type === 'form-data') {
175182
node.data = {
176183
...node.data,
177184
requestBody: {

0 commit comments

Comments
 (0)