-
-
Notifications
You must be signed in to change notification settings - Fork 328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a new query panel #2393
Add a new query panel #2393
Conversation
Fixed both of those; the resizing was a strange bug I didn't completely investigate but got around for now. The page parameters dialog I think should be allowed to coexist with an open panel, so I made that behaviour happen. |
@@ -40,11 +40,11 @@ const FUTURE_COMPONENTS = new Map<string, FutureComponentSpec>([ | |||
['Radio', { url: 'https://github.com/mui/mui-toolpad/issues/744', displayName: 'Radio' }], | |||
]); | |||
|
|||
const WIDTH_COLLAPSED = 40; | |||
export const COMPONENT_CATALOG_WIDTH_COLLAPSED = 40; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to export.
export const COMPONENT_CATALOG_WIDTH_COLLAPSED = 40; | |
const COMPONENT_CATALOG_WIDTH_COLLAPSED = 40; |
@@ -73,15 +75,21 @@ const EditableText = React.forwardRef<HTMLInputElement, EditableTextProps>( | |||
|
|||
const handleChange = React.useCallback( | |||
(event: React.ChangeEvent<HTMLInputElement>) => { | |||
if (readOnly) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this gets called while the input is readonly, that would mean there is an error in the code, right?
In that case, would it make more sense to make this an invariant?
if (readOnly) { | |
invariant(!readOnly, 'Readonly input should be disabled') |
(Same below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went through all the files. This looks great! I sprinkled a few comments here and there, nothing blocking.
@@ -563,8 +563,12 @@ function parseBindings( | |||
} | |||
|
|||
if (appDom.isQuery(elm)) { | |||
let kind: 'query' | 'action' = 'query'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remark Is there value in introducing another name for "mutation"? We could also keep calling it "mutation" in the code and just use "Action" as a display name? It could keep the code simpler.
I'm marking this with Remark. It's just a stylistic comment on the code. These are just things that go through my head when reading the code. Doesn't require a change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood, I added "action" as a way to progressively perhaps do away with mutation in the code and eventually create a separate node type called "action", since if that is what we want to call it in the UI, should it also not be what we describe it in the codebase for simplicity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I prefer either one way or the other:
- either we call it "action" everywhere and just do a translation to "mutation", right before we read/write to a file
- or we call it "mutation" everywhere and name it "action" just right before we display in the UI
The reason is that this prevents us from mismatching and constant translation all around the codebase. Ideally if the rename is done in a large PR, you start with the latter, just to avoid introducing many unnecessary chamges to unrelated code. Then in a separate PR update the codebase to the former.
But, leave it for now, I marked it as a "note" it's not blocking the merge of this PR.
onSelectNode?.(toolpadNodeId); | ||
}, [onSelectNode, toolpadNodeId]); | ||
|
||
const queryCreationMode = React.useMemo(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note Ideally, the DataTreeItem
doesn't know if it's a query or a mutation. In a true abstraction, we should be able to introduce a third query type without needing to alter DataTreeItem
.
Also, I don't fully understand, do all tree nodes in the queries tree have the same node id? ids must be unique, otherwise we can't refer to the individual items in the treeview APIs. And in an ideal world they are opaque as well. (Meaning: we can't gain any knowledge about the object just by inspecting the id on its own)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, the DataTreeItem doesn't know if it's a query or a mutation. In a true abstraction, we should be able to introduce a third query type without needing to alter DataTreeItem.
Agreed, naming it DataTreeItem
was an attempt to begin making it agnostic to the type of item represented but I imagine that'll have to be a separate effort distinct from this one or any future improvements scoped to the query panel
Also, I don't fully understand, do all tree nodes in the queries tree have the same node id? ids must be unique, otherwise we can't refer to the individual items in the treeview APIs. And in an ideal world they are opaque as well. (Meaning: we can't gain any knowledge about the object just by inspecting the id on its own)
The node ids are separate for each node
import { BindableAttrValue, LiveBinding } from '@mui/toolpad-core'; | ||
import { useBrowserJsRuntime } from '@mui/toolpad-core/jsBrowserRuntime'; | ||
import invariant from 'invariant'; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
}} | ||
> | ||
<Stack | ||
display={'grid'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking for a lint rule that can catch these
display={'grid'} | |
display="grid" |
value={draft?.attributes?.mode ?? 'query'} | ||
onChange={handleModeChange} | ||
sx={{ | ||
'& .MuiInputLabel-root': { fontSize: 12 }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'& .MuiInputLabel-root': { fontSize: 12 }, | |
[`& .${inputLabelClasses.root}`]: { fontSize: 12 }, |
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed this all around the PR, but I only mentioned it once.
Non-blocking as well
@@ -81,7 +81,7 @@ function QueryEditor({ | |||
|
|||
const handleSpreadsheetChange = React.useCallback( | |||
(event: React.SyntheticEvent<Element, Event>, newValue: GoogleDriveFile | null) => { | |||
setInput((existing) => { | |||
setInput?.((existing) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this change? What is the purpose of a query editor without an onChange
prop defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onChange
functionality for the new query editor client started living in the appStateApi
, so we don't need to pass in an onChange
handler prop anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But functionally, how should it behave when no handler is defined. What's the use-case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function selection control belongs in its own file.
@@ -1,8 +1,9 @@ | |||
import { BindableAttrValue, ExecFetchResult, PrimitiveValueType } from '@mui/toolpad-core'; | |||
import type { IntrospectionResult } from '../../server/functionsTypesWorker'; | |||
|
|||
export interface LocalConnectionParams {} | |||
export type FileTypes = 'query' | 'component' | 'page' | 'theme'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this being used?
export type FileTypes = 'query' | 'component' | 'page' | 'theme'; |
@@ -12,6 +12,7 @@ function MySQLConnectionParamsInput({ | |||
|
|||
const dataSource: ClientDataSource<SqlConnectionParams, SqlQuery> = { | |||
displayName: 'MySQL', | |||
isEnabled: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this property, why not just omit them where we're exporting all the datasources?
in any case, everywhere in the codebase "enabled"/"disabled" seem one of this names where the "is" prefix is always implied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, this can be done without
@@ -18,6 +18,8 @@ export const TOOLPAD_BRIDGE_GLOBAL = '__TOOLPAD_BRIDGE__'; | |||
|
|||
export const VERSION_CHECK_INTERVAL = 1000 * 60 * 10; | |||
|
|||
export const PAGE_PANEL_WIDTH = 250; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const PAGE_PANEL_WIDTH = 250; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean directly defining the width in the consuming file instead of creating a constant for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's still a constant, but no need for it globally. It's used in only one file. And it's the sort of thing that probably should always be local.
if (readOnly) { | ||
return; | ||
} | ||
invariant(!readOnly, 'Readonly input should be disabled'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's another instance in this file where this is happening
@@ -29,25 +41,16 @@ export default function QueryIcon({ id: iconId, mode, sx }: QueryIconProps) { | |||
const ModeIcon = modeIconMap.get(mode ?? ''); | |||
|
|||
return ( | |||
<div style={{ display: 'flex' }}> | |||
<div style={{ display: 'flex', gap: DataSourceIcon ? 0.5 : 0 }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<div style={{ display: 'flex', gap: DataSourceIcon ? 0.5 : 0 }}> | |
<div style={{ display: 'flex', gap: 0.5 }}> |
The gap has no effect if there is only one child
…olpad into query-explorer-left
Closes: #2138
Screen.Recording.2023-10-24.at.8.33.57.PM.mov
TODO
https://excalidraw.com/#json=AQ-1k8DkwEVa2RUtGCQMG,TwZB3D-vbIgvCysL-mJ8mw
Mode: Add a label in the explorer to distinguish automatic and manual queries and create separate buttons for creating them
Settings: Either use tabs, or a settings cog icon which switches the display to settings, the icon switching to close. Explorer either experience and choose one.
Response tabs: The preview should be one tab in a set which could include the network response etc. in a vertically expandable section.
Scrolling: Panel should be expandable vertically.
Tests: Edit tests to adapt to query panel
Saving: Bunch changes to the queries and apply via
Ctrl/Cmd + S
.Unsaved changes: Detect click-away from the query tab and show a dialog to manage unsaved changes; Show an unsaved changes indicator on query tab
Order of panels: Pages, Page Hierarchy, Queries, Actions
Make Actions/Queries separate sections
“Save” button in the top panel, "Preview" button in the Preview/Dev Tools panel
When creating a new query, it immediately saves the query to the project. Instead it should create it in draft and let me save or discard it when I'm done. There shouldn't be any query in the project because that makes the runtime immediately start executing it. This is how it currently works and I believe we should keep this behavior.
Differentiate queries/actions in binding modal
Scroll to new file on “New File” click
Fix functions autocomplete anchor position?
Try and reduce the attention the sub-tabs draw