-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
feat(dashboard): step conditions editor ui #7502
base: next
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for dev-web-novu ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
✅ Deploy Preview for dashboard-v2-novu-staging ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
|
||
import { Button } from '@/components/primitives/button'; | ||
|
||
export const AddConditionAction = ({ label, title, rules, handleOnClick }: ActionWithRulesAndAddersProps) => { |
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 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.
What do you think about the And icon? I feel it's a little confusing and feels like a reload, what if we just remove it? another UX tweak, we can hide if there is only one condition available, and show it if more than one was added.
cc. @twentyone24
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, the icon feels a bit odd, I'll change it to a default double arrows. I wouldn't do the conditional hiding as it will introduce the UI jumping effect.
import { StackedPlusLine } from '@/components/icons/stacked-plus-line'; | ||
import { Button } from '@/components/primitives/button'; | ||
|
||
export const AddGroupAction = ({ label, title, level, rules, handleOnClick }: ActionWithRulesAndAddersProps) => { |
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.
import { cn } from '@/utils/ui'; | ||
import { toSelectOptions } from '@/components/conditions-editor/select-option-utils'; | ||
|
||
export const CombinatorSelector = ({ disabled, value, options, handleOnChange }: CombinatorSelectorProps) => { |
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.
getParentGroup: () => null, | ||
}); | ||
|
||
export function ConditionsEditorProvider({ children }: { children: React.ReactNode }) { |
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 context provider for the editor. It stores the query, allows to remove and clone condition/group, and get the nearest parent.
const fields = [ | ||
{ name: 'payload.foo', label: 'payload.foo', value: 'payload.foo' }, | ||
{ name: 'payload.bar', label: 'payload.bar', value: 'payload.bar' }, | ||
{ name: 'payload.baz', label: 'payload.baz', value: 'payload.baz' }, | ||
{ name: 'payload.qux', label: 'payload.qux', value: 'payload.qux' }, | ||
{ name: 'payload.quux', label: 'payload.quux', value: 'payload.quux' }, | ||
{ name: 'payload.corge', label: 'payload.corge', value: 'payload.corge' }, | ||
{ name: 'payload.grault', label: 'payload.grault', value: 'payload.grault' }, | ||
{ name: 'payload.garply', label: 'payload.garply', value: 'payload.garply' }, | ||
{ name: 'payload.waldo', label: 'payload.waldo', value: 'payload.waldo' }, | ||
{ name: 'payload.fred', label: 'payload.fred', value: 'payload.fred' }, | ||
{ name: 'payload.plugh', label: 'payload.plugh', value: 'payload.plugh' }, | ||
{ name: 'payload.xyzzy', label: 'payload.xyzzy', value: 'payload.xyzzy' }, | ||
{ name: 'payload.thud', label: 'payload.thud', value: 'payload.thud' }, | ||
] satisfies Field[]; | ||
|
||
export const variables = fields.map((field) => ({ type: 'variable', label: field.label })) satisfies LiquidVariable[]; |
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.
These are just the examples for now, will be removed when we integrate with the API.
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.
Will those come from the combination of all the payloads used on the workflow? This also has to contain things like previous steps, etc... Can we reuse the exact logic of the variables autocomplete context we give in the code mirror?
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, that is done in the next PR: https://github.com/novuhq/novu/pull/7526/files#r1918224924
|
||
return ( | ||
<div className="flex flex-col"> | ||
<input |
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 hidden input manages the focus and keyboard navigation. I've tried with cmdk,
but it doesn't work well and doesn't allow you to control the focus. Let me explain: when the input is focused, we want to show the dropdown but not focus on it so the input stays focused, then, when the keyboard navigation is used, we want to focus on the list and navigate through the list items.
<KeyboardItem>↑</KeyboardItem> | ||
<KeyboardItem>↓</KeyboardItem> | ||
<span className="text-foreground-600 text-paragraph-xs ml-0.5">Navigate</span> | ||
<KeyboardItem className="ml-auto">↵</KeyboardItem> |
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 for svg's :P
const [inputValue, setInputValue] = useState(value ?? ''); | ||
const [filterValue, setFilterValue] = useState(''); |
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 filter value is used to filter the options array. When the input is focused it's value is reset to ''
and all the items are shown to the user, otherwise if input value is typed it will filter the list.
<path d="M13.662 15.68c-.499.28-1.045.37-1.412.37a.55.55 0 0 1 0-1.1c.217 0 .57-.06.875-.23.288-.16.515-.407.583-.81.051-.31.054-.678.058-1.116v-.056c.005-.439.011-.941.1-1.404.09-.467.275-.958.683-1.328.415-.375.985-.556 1.701-.556a.55.55 0 0 1 0 1.1c-.533 0-.808.132-.963.272-.162.147-.274.374-.34.72-.068.35-.076.753-.08 1.205l-.001.089c-.003.406-.007.857-.073 1.255-.133.797-.614 1.3-1.13 1.59ZM8.111 8.111a.55.55 0 0 1 .778 0l3 3a.55.55 0 1 1-.778.778l-3-3a.55.55 0 0 1 0-.778Z" /> | ||
<path d="M11.89 8.111a.55.55 0 0 0-.779 0l-3 3a.55.55 0 1 0 .778.778l3-3a.55.55 0 0 0 0-.778Z" /> | ||
</g> | ||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}> |
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.
improved this svg to be able to better control sizes
@@ -183,10 +185,10 @@ export const Editor = React.forwardRef<{ focus: () => void; blur: () => void }, | |||
// which results in value not being updated and "jumping" effect in the editor | |||
// to prevent this we need to flush the state updates synchronously | |||
flushSync(() => { | |||
onChange?.(value); | |||
onChangeRef.current?.(value); |
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 performance improvement for the CodeMirror Editor. When the onChange
prop changes, usually on every render it has a slow performance and lag, this approach fixes it.
apps/dashboard/src/components/conditions-editor/operator-selector.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/components/conditions-editor/value-editor.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/components/conditions-editor/variable-select.tsx
Outdated
Show resolved
Hide resolved
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.
On the initial screen open we see 3 buttons, what if we just show one in the empty state? Or maybe craft an empty state that explains the basics of conditions? cc. @twentyone24
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.
Showing one rule will require picking up some values for field, operator, value
inputs which will be tricky.
The library does not support the empty state, and there is no API for setting an empty component, unfortunately. We can hack it, but it will require building the layout for all buttons, rules, and actions from the ground.
I think that if we supply good documentation about it, it should be enough.
apps/dashboard/src/components/conditions-editor/variable-select.tsx
Outdated
Show resolved
Hide resolved
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.
Looks really exciting! Left a few notes and comments, other than that it works great from manual testing perspective, ofcourse will test more once it's integrated e2e
What changed? Why was the change needed?
The Step Conditions - just the editor UI part. It will be integrated with API in the upcoming PRs.
The components used to build the editor look a bit different from what we had in the designs because the sizes used in the designs are not the ones that we support in the code, and not all component behavior was shown.
Also, improved performance of the CodeMirror
Editor
component.Screenshots
Screen.Recording.2025-01-14.at.11.58.48.mov