• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alkem-io / client-web / #8389

18 Jul 2024 07:25AM UTC coverage: 5.75%. First build
#8389

Pull #6612

travis-ci

Pull Request #6612: Package updates to reduce vulnerabilities

188 of 9965 branches covered (1.89%)

Branch coverage included in aggregate %.

0 of 13 new or added lines in 7 files covered. (0.0%)

1407 of 17775 relevant lines covered (7.92%)

0.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/core/ui/forms/MarkdownInputControls/ToggleLinkButton.tsx
1
import { Button } from '@mui/material';
2
import { Editor } from '@tiptap/react';
3
import { useEffect, useRef, useState } from 'react';
4
import { useTranslation } from 'react-i18next';
5
import { Form, Formik } from 'formik';
6
import * as yup from 'yup';
7
import FormikInputField from '../FormikInputField/FormikInputField';
8
import { LinkOutlined } from '@mui/icons-material';
9
import DialogHeader from '@/core/ui/dialog/DialogHeader';
10
import Gutters from '@/core/ui/grid/Gutters';
11
import { Actions } from '@/core/ui/actions/Actions';
12
import DialogWithGrid from '@/core/ui/dialog/DialogWithGrid';
13
import { useNotification } from '@/core/ui/notifications/useNotification';
14
import { Selection } from 'prosemirror-state';
15
import { BlockTitle } from '@/core/ui/typography';
16
import MarkdownInputToolbarButton, { MarkdownInputToolbarButtonProps } from './MarkdownInputToolbarButton';
17
import { urlValidator } from '../validator/urlValidator';
18
import { MID_TEXT_LENGTH } from '../field-length.constants';
19

20
interface ToggleLinkButtonProps extends Omit<MarkdownInputToolbarButtonProps, 'tooltip'> {
21
  editor: Editor | null;
22
  onDialogOpen?: () => void;
23
  onDialogClose?: () => void;
24
}
25

26
type LinkProps = { href: string };
×
27

×
28
const ToggleLinkButton = ({ editor, onDialogOpen, onDialogClose, ...buttonProps }: ToggleLinkButtonProps) => {
×
29
  const { t } = useTranslation();
30
  const wasFocusedRef = useRef<boolean>(false);
×
31

×
32
  const subscribeToFocus = (editor: Editor) => {
×
33
    const markAsFocused = () => {
34
      wasFocusedRef.current = true;
35
    };
×
36

37
    editor.on('focus', markAsFocused);
×
38

×
39
    return () => {
40
      editor.off('focus', markAsFocused);
41
    };
42
  };
×
43

×
44
  useEffect(() => {
×
45
    if (editor) {
46
      subscribeToFocus(editor);
47
    }
48
  }, [editor]);
×
49

50
  const [isDialogOpen, setIsDialogOpen] = useState(false);
×
51

×
52
  const openDialog = () => {
×
53
    onDialogOpen?.();
54
    setIsDialogOpen(true);
55
  };
×
56

×
57
  const closeDialog = () => {
×
58
    setIsDialogOpen(false);
×
59
    editor?.commands.focus();
60
    onDialogClose?.();
61
  };
×
62

63
  const notify = useNotification();
×
64

×
65
  const markAsLink = (linkProps: LinkProps) => {
×
66
    if (!editor) {
67
      return;
68
    }
×
69

70
    const { selection } = editor.state;
×
71

×
72
    try {
×
73
      if (!selection.empty) {
74
        editor.commands.setLink(linkProps);
75
      } else {
×
76
        // If the input hasn't been focused once, the link is inserted at the end.
×
77
        const from = wasFocusedRef.current ? selection.from : Selection.atEnd(editor.state.doc).from;
78
        const to = from + linkProps.href.length;
×
79

80
        editor
81
          .chain()
82
          .setTextSelection(from)
83
          .insertContent(linkProps.href)
84
          .setTextSelection({ from, to })
85
          .setLink(linkProps)
86
          .setTextSelection(to)
87
          .run();
NEW
88
      }
×
NEW
89
    } catch (error) {
×
90
      if (error instanceof Error) {
91
        notify(error.message, 'error');
×
92
      }
93
      throw error;
94
    }
×
95

96
    closeDialog();
97
  };
×
98

99
  const unmarkAsLink = () => editor?.chain().focus().unsetLink().run();
×
100

101
  const initialValues: LinkProps = { href: 'https://' };
102

103
  const validationSchema = yup.object().shape({
×
104
    href: urlValidator({ maxLength: MID_TEXT_LENGTH, required: true }),
105
  });
×
106

107
  const isDisabled = !editor || !editor.can().toggleLink(initialValues);
×
108

109
  const isActive = editor?.isActive('link');
×
110

111
  const handleClick = isActive ? unmarkAsLink : openDialog;
112

113
  return (
114
    <>
×
115
      <MarkdownInputToolbarButton
116
        onClick={handleClick}
117
        disabled={isDisabled}
118
        color={isActive ? 'secondary' : undefined}
119
        tooltip={t('components.wysiwyg-editor.toolbar.link.link')}
120
        {...buttonProps}
121
      >
122
        <LinkOutlined />
123
      </MarkdownInputToolbarButton>
124
      <DialogWithGrid open={isDialogOpen} onClose={closeDialog} aria-labelledby="toggle-link-dialog-title">
125
        <DialogHeader onClose={closeDialog}>
126
          <BlockTitle id="toggle-link-dialog-title">{t('components.wysiwyg-editor.link.dialogHeader')}</BlockTitle>
127
        </DialogHeader>
128
        <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={markAsLink}>
129
          <Form>
130
            <Gutters>
131
              <FormikInputField title={t('common.url')} name="href" />
132
              <Actions justifyContent="space-between">
133
                <Button onClick={closeDialog}>{t('buttons.cancel')}</Button>
134
                <Button type="submit" variant="contained">
135
                  {t('buttons.insert')}
136
                </Button>
137
              </Actions>
138
            </Gutters>
139
          </Form>
140
        </Formik>
141
      </DialogWithGrid>
142
    </>
143
  );
144
};
145

146
export default ToggleLinkButton;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc