-
-
Notifications
You must be signed in to change notification settings - Fork 317
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
Allow nested bindings #2114
Allow nested bindings #2114
Conversation
8b5d8ec
to
603ea4e
Compare
603ea4e
to
1ad2316
Compare
The preview/production runtime seem to turn all |
8f6ac7a
to
4a50e12
Compare
Fixing the double dollar problem in #2165. We do need to think about a good migration strategy going forward, but I'm not sure we should mix that in here if we don't have to, there's a lot to consider. |
I've added changing bindings from {
type: 'jsExpression'
value: string
} to {
$$jsExpression: string
} as proposed. Also applies to It's a lot of changes, hopefully nothing else is broken, I could not find anything else so far. Choices made that could deserve some attention/discussion:
|
As for testing nested bindings we don't really have a use case for nested bindings yet so I think we can cover it with the chart component tests once that component is added. |
@@ -12,7 +12,8 @@ spec: | |||
props: | |||
onClick: | |||
$$navigationAction: | |||
page: au03sef | |||
page: | |||
$ref: au03sef |
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.
This doesn't seem right. page
is supposed to be a string according to the schema: https://github.com/mui/mui-toolpad/blob/ae82d58325f769644a7fb0f2f0b6b4683e75d285/packages/toolpad-app/src/server/schema.ts#L58
I'd expect a test to break because of this, I thought Toolpad wouldn't start up when there's an invalid file 🤔
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 think the code itself was expecting a ref, anyway using a string seems good - I'll make it a string and take a look at the schema validation.
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.
It's not easy to make the schema validation work here, seems like z.record(jsonSchema)
is allowing any object to pass validation, but without it the unions also don't work as expected. So far haven't solved it, I'm looking into it.
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.
ok i finally found a way
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.
Aah good catch,
One thing we could do is disallow properties in the jsonSchema
that start with $$
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
type Literal = z.infer<typeof literalSchema>;
type Json = Literal | { [key: string]: Json } | Json[];
export const jsonSchema: z.ZodType<Json> = z
.lazy(() =>
z.union([
...literalSchema.options,
z.array(jsonSchema),
// disallow $$ properties here
z.record(jsonSchema).refine((obj) => !Object.keys(obj).some((key) => key.startsWith('$$')))
]),
)
But this would ofcourse not work for nested bindings, so we need to pull back in jsExpressionBindingSchema
. But then we should probably not call it jsonSchema
anymore. We can probably instead remove jsonSchema
from bindablePropSchema
and insteadcreate that type like. (You'll have to do some trickery to make recursive types work, see how it's done in jsonSchema
)
export const bindablePropSchema = z.lazy(() => z.union([
...literalSchema.options,
z.array(bindablePropSchema),
z.record(bindablePropSchema).refine((obj) => !Object.keys(obj).some((key) => key.startsWith('$$'))),
jsExpressionBindingSchema,
envBindingSchema,
jsExpressionActionSchema,
navigationActionSchema,
templateSchema,
]));
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.
Yep this is exactly what I was already doing, I was just going to try to pass all the possible $$...
properties to exclude in the refine function, but we can also just exclude if they start with $$
, maybe that's easier to maintain.
|
||
const versions = [v1, v2, v3, v4, v5, v6]; |
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: We will remove migrations post 0.1.16.
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.
Ok, the one I added was just to help if there's any scenario where it's needed, but we can remove it later along with all these migrations.
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.
yass!
@@ -78,15 +78,15 @@ function ConnectionEditorContent<P>({ | |||
<ConnectionContextProvider value={connectionEditorContext}> | |||
<ConnectionParamsEditor | |||
dataSource={dataSource} | |||
value={connectionNode.attributes.params.value} | |||
value={connectionNode.attributes.params.$$secret} |
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.
yep, secret is obsolete in post-13
|
||
const nestedBindingResultValue = results[nestedBindingId]?.value; | ||
if (nestedBindingResultValue) { | ||
bindingResult = updatePath( |
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'd prefer this written with nested update
calls as we do everywhere else. It's slightly more verbose but it's more type-safe, typescript will yell at us if we update the AppDom
but forget to update this statement.
It's probably slightly more performant as well and it doesn't create clones if it doesn't need to.
We can always start using immer
to simplify these sort of updates ina type safe way.
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.
Got it, I'll try to use update
for now.
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 reason I created and used an updatePath
method is because lodash makes it easy to update an object by passing a string with the path of the property we want to change (it also needs clone
to be immutable, though). And in this case we could have a unknown number of nested objects or arrays.
I could create an issue to write our own method for this later using our own update
instead of lodash methods? We would need to parse the path manually.
Looking good, just this one comment seems a blocker to me |
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.
Lovely
Allow for nested bindings, which is needed for example to have the chart component work as we intend to (with nested data series that can be bound to different datasources).
Extracted from #2081
Tried many different approaches, this one:
Open for feedback of course!
Also not sure if I might have missed some edge cases or possible bugs with the parts of the code I altered.