Skip to content

feat(dashboard): email editor variable suggestions dropdown, pill, editing #7625

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

Merged
merged 15 commits into from
Feb 5, 2025

Conversation

LetItRock
Copy link
Contributor

What changed? Why was the change needed?

Reuse and align the variable component in the email editor.

  • variable pill component
  • reused edit variable popover
  • reused variable dropdown list

Screenshots

Screenshot 2025-01-30 at 16 42 29

Screenshot 2025-01-30 at 16 42 17

Screen.Recording.2025-01-30.at.16.41.13.mov

Copy link

linear bot commented Jan 30, 2025

Copy link

netlify bot commented Jan 30, 2025

Deploy Preview for dev-web-novu ready!

Name Link
🔨 Latest commit b92385c
🔍 Latest deploy log https://app.netlify.com/sites/dev-web-novu/deploys/67a31e65edf9340008a1ec05
😎 Deploy Preview https://deploy-preview-7625.dashboard.novu-staging.co
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Jan 30, 2025

Deploy Preview for dashboard-v2-novu-staging ready!

Name Link
🔨 Latest commit b92385c
🔍 Latest deploy log https://app.netlify.com/sites/dashboard-v2-novu-staging/deploys/67a31e6534257500082353b9
😎 Deploy Preview https://deploy-preview-7625.dashboard-v2.novu-staging.co
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@@ -23,7 +23,7 @@
"@codemirror/autocomplete": "^6.18.3",
"@hookform/resolvers": "^3.9.0",
"@lezer/highlight": "^1.2.1",
"@maily-to/core": "^0.0.22",
"@maily-to/core": "file:./maily-to-core-0.0.26.tgz",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

temporarily until the arikchakma/maily.to#121 is merged

hoveredOptionIndex: number;
};

const VariablesList = React.forwardRef<HTMLUListElement, VariablesListProps>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to a reusable component, which is used later as the Email editor suggestions dropdown for variables

@@ -130,53 +64,6 @@ export const VariableSelect = ({
}
};

const scrollToOption = (index: number) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scrollToOption, next, prev moved to reusable hook that manages the keyboard navigation useListKeyboardNavigation

const [hoveredOptionIndex, setHoveredOptionIndex] = useState(0);
const variablesListRef = useRef<HTMLUListElement>(null);

const { variablesListRef, hoveredOptionIndex, next, prev, reset, focusFirst } = useListKeyboardNavigation({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: better name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a a separate hook for this? Since variables-list is a reusable component I'd expect the hook to live in there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we need it as it allows to control the list from the parent... for example MailyVariablesList has the useImperativeHandle with onKeyDown which is called by it's parent

@@ -117,21 +116,18 @@ export function ControlInput({
value={value}
onChange={onChange}
/>
<Popover open={!!selectedVariable} onOpenChange={handleOpenChange}>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to reusable component that later is used in the Email editor when editing the variable

Comment on lines 25 to 47
useImperativeHandle(ref, () => ({
onKeyDown: ({ event }: { event: KeyboardEvent }) => {
if (event.key === 'ArrowUp') {
event.preventDefault();
prev();
return true;
}

if (event.key === 'ArrowDown') {
event.preventDefault();
next();
return true;
}

if (event.key === 'Enter') {
onSelect(options[hoveredOptionIndex].value ?? '');
reset();
return true;
}

return false;
},
}));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onKeyDown is called by Maily core which is used by ref passed.

import { VARIABLE_REGEX_STRING } from '@/components/primitives/control-input/variable-plugin';
import { VariablePill } from '@/components/variable/variable-pill';

export function VariableView({ node, updateAttributes, editor }: NodeViewProps) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component represents the variable pill plus the editing popover. It's used as a Maily variable extension view.

@@ -39,6 +46,85 @@ export const Maily = ({ value, onChange, className, ...rest }: MailyProps) => {
const isForBlockEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_ND_EMAIL_FOR_BLOCK_ENABLED);
const isShowEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_ND_EMAIL_SHOW_ENABLED);

const calculateVariables = useCallback(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved from the Maily prop and reused in the variable extension

Comment on lines +115 to +124
VariableExtension.extend({
addNodeView() {
return ReactNodeViewRenderer(VariableView, {
className: 'relative inline-block',
as: 'div',
});
},
}).configure({
suggestion: getVariableSuggestions(calculateVariables, VARIABLE_TRIGGER_CHARACTER, MailyVariablesList),
}),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's how you override the default Maily variable and suggestions

Comment on lines +52 to +55
// IMPORTANT: do not close the sheet if the interact outside is from the maily variable list
if (e.target instanceof HTMLDivElement && e.target.className.includes('tippy-box')) {
return;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem: Maily renders the variables dropdown with tippy which renders it outside the normal tree flow and at the bottom inside the body. Clicking inside of the list is treated as clicking outside the sidebar, which results in closing the sidebar.
I wasn't able to find a better solution to this.

const [hoveredOptionIndex, setHoveredOptionIndex] = useState(0);
const variablesListRef = useRef<HTMLUListElement>(null);

const { variablesListRef, hoveredOptionIndex, next, prev, reset, focusFirst } = useListKeyboardNavigation({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a a separate hook for this? Since variables-list is a reusable component I'd expect the hook to live in there.

className={`bg-url-code bg-repeat-no-repeat h-[calc(1em-2px)] w-[calc(1em-2px)] min-w-[calc(1em-2px)] bg-[url("/images/code.svg")] bg-contain bg-center`}
></span>
<span className="leading-[1.2]">{variable}</span>
{hasFilters && <span className="bg-feature-base h-[0.275em] w-[0.275em] rounded-full" />}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit-picky one, we can do the dot with an after pseudo element to remove the need of an extra span.


import { useState } from 'react';

export const useVariableListKeyboardNavigation = ({ maxIndex }: { maxIndex: number }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move the hook inside the component, the file is not that big in total, <200 lines.

@LetItRock LetItRock merged commit fc18806 into next Feb 5, 2025
32 checks passed
@LetItRock LetItRock deleted the nv-5174-maily-variable-pill-list-editing branch February 5, 2025 08:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants