-
Notifications
You must be signed in to change notification settings - Fork 2
feat/COMPASS-9935 collapse field level #167
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
Conversation
Co-authored-by: Sergey Petushkov <[email protected]>
Co-authored-by: Sergey Petushkov <[email protected]>
455050c to
91e4166
Compare
|
Thank you for the feedback @gribnoysup ! I think I addressed everything we discussed and it does make more sense now. Can you take another look? |
gribnoysup
left a comment
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.
Left a few comments, but nothing blocking for this PR, just some things to consider. Good work and thanks for having a productive technical discussion on the topic, I think this helped to find a really good approach to this.
|
|
||
| const { onChangeFieldName, onChangeFieldType, fieldTypes, onFieldExpandToggle } = useEditableDiagramInteractions(); | ||
|
|
||
| const hasCollapseFunctionality = !!onFieldExpandToggle; |
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.
Absolutely not suggesting you need to do something about it, but something about the inconsistency of the variable saying "collapse", but the public method saying "expand" really made me pause here for a sec 😂
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.
That's fair, let me rename it so that it doesn't confuse anyone
| const onNodeExpandToggle = useCallback((_evt: ReactMouseEvent, nodeId: string, expanded: boolean) => { | ||
| setExpanded(state => { | ||
| return { | ||
| ...state, | ||
| [nodeId]: !state[nodeId], | ||
| [nodeId]: { | ||
| node: expanded, | ||
| fields: {}, | ||
| }, | ||
| }; | ||
| }); | ||
| }, []); | ||
|
|
||
| const onFieldExpandToggle = useCallback((_evt: ReactMouseEvent, nodeId: string, fieldId: FieldId) => { | ||
| const key = getExpandedFieldKey(fieldId); | ||
| setExpanded(state => { | ||
| const prevNodeState = state[nodeId] ?? {}; | ||
| const prevFieldState = isFieldExpanded(state, nodeId, fieldId); | ||
| return { | ||
| ...state, | ||
| [nodeId]: { | ||
| ...prevNodeState, | ||
| fields: { | ||
| ...(prevNodeState.fields ?? {}), | ||
| [key]: !prevFieldState, | ||
| }, | ||
| }, | ||
| }; | ||
| }); | ||
| }, []); |
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 is slightly tangential, but want to call this out (because it's relevant for how to model this on compass side also):
I think instead of having expanded as an extra state on node in addition to fields having an expanded state, we should treat "expand / collapse all" as an action that just affects all fields, so instead of keeping this expanded state here for the node, I would suggest you update the fields directly. I think you'll see that this will simplify the logic in the callback props, and will remove the need to do some extra processing for the nodes before passing them down to the diagram component.
Following is not a fully tested suggestion, but kinda close to the real one:
| const onNodeExpandToggle = useCallback((_evt: ReactMouseEvent, nodeId: string, expanded: boolean) => { | |
| setExpanded(state => { | |
| return { | |
| ...state, | |
| [nodeId]: !state[nodeId], | |
| [nodeId]: { | |
| node: expanded, | |
| fields: {}, | |
| }, | |
| }; | |
| }); | |
| }, []); | |
| const onFieldExpandToggle = useCallback((_evt: ReactMouseEvent, nodeId: string, fieldId: FieldId) => { | |
| const key = getExpandedFieldKey(fieldId); | |
| setExpanded(state => { | |
| const prevNodeState = state[nodeId] ?? {}; | |
| const prevFieldState = isFieldExpanded(state, nodeId, fieldId); | |
| return { | |
| ...state, | |
| [nodeId]: { | |
| ...prevNodeState, | |
| fields: { | |
| ...(prevNodeState.fields ?? {}), | |
| [key]: !prevFieldState, | |
| }, | |
| }, | |
| }; | |
| }); | |
| }, []); | |
| const onNodeExpandToggle = useCallback((_evt: ReactMouseEvent, nodeId: string, expanded: boolean) => { | |
| setNodes(nodes => | |
| nodes.map(node => | |
| node.id === nodeId | |
| ? { | |
| ...node, | |
| fields: node.fields.map(field => { | |
| return { ...field, expanded } | |
| }), | |
| } | |
| : node, | |
| ), | |
| ); | |
| }, []); | |
| const onFieldExpandToggle = useCallback((_evt: ReactMouseEvent, nodeId: string, fieldId: FieldId) => { | |
| setNodes(nodes => | |
| nodes.map(node => | |
| node.id === nodeId | |
| ? { | |
| ...node, | |
| fields: fields.map(field => { | |
| return field.id === fieldId | |
| ? { ...field, expanded: !field.expanded } | |
| : field | |
| }), | |
| } | |
| : node, | |
| ), | |
| ); | |
| }, []); |
src/types/component-props.ts
Outdated
| * Called when the button to expand / collapse a single field is clicked | ||
| * header. | ||
| */ | ||
| export type OnFieldExpandHandler = (event: ReactMouseEvent, nodeId: string, fieldPath: string[]) => void; |
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.
Not sure it's worth it, but maybe easier then on the other side instead and more consistent with OnNodeExpandHandler where we have to provide this value
| export type OnFieldExpandHandler = (event: ReactMouseEvent, nodeId: string, fieldPath: string[]) => void; | |
| export type OnFieldExpandHandler = (event: ReactMouseEvent, nodeId: string, fieldPath: string[], shouldExpand: boolean) => void; |
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.
Sure, why not!
src/utilities/convert-nodes.ts
Outdated
| function hasChildren(fields: NodeField[], index: number): boolean { | ||
| const fieldDepth = fields[index].depth ?? 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.
Suuuuper nit, you're calling this in a place where we have the field already, so instead of picking it from an array by index, might just pass it down (and then this function matches the map function interface and this is slightly satisfying)
| function hasChildren(fields: NodeField[], index: number): boolean { | |
| const fieldDepth = fields[index].depth ?? 0; | |
| function hasChildren(field: NodeField, index: number, fields: NodeField[]): boolean { | |
| const fieldDepth = field.depth ?? 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.
Okay, the map function interface analogy really sold it :D
External Links
🎟️
Description
Changes:
onNodeExpandToggle, the client can provideonFieldExpandToggleNodeField, the client can provideexpandedexpandableis calculated internally, but the toggle is only displayed ifonFieldExpandToggleis providedsource|targetFieldIdinstead ofsource|targetFieldIndex. the latter isn't suitable anymore because not all fields are visibleNote: Expandability depends on the existence of children more than on the direct types (for example there can be an object without children, or an array/mixed field that does have children).
TODO
📸 Screenshots/Screencasts
Node with collapse/expand functionality
Node without collapse/expand functionality
Top level and field level collapse behaviour
Screen.Recording.2025-11-20.at.13.52.36.mov