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

alkem-io / client-web / #10064

27 Jan 2025 07:46PM UTC coverage: 5.712%. First build
#10064

Pull #7531

travis-ci

Pull Request #7531: updated to retrieve iframeAllowedUrls + use in one place

190 of 11094 branches covered (1.71%)

Branch coverage included in aggregate %.

1 of 8 new or added lines in 3 files covered. (12.5%)

1530 of 19020 relevant lines covered (8.04%)

0.18 hits per line

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

0.0
/src/core/ui/forms/MarkdownInputControls/InsertEmbedCodeButton/InsertEmbedCodeButton.tsx
1
import { useRef, useState, useEffect, ChangeEvent, useMemo } from 'react';
2

3
import { Form, Formik } from 'formik';
4
import { Editor } from '@tiptap/react';
5
import { useTranslation } from 'react-i18next';
6
import { Button, styled, TextareaAutosize } from '@mui/material';
7
import { useNotification } from '@/core/ui/notifications/useNotification';
8
import SmartScreenOutlinedIcon from '@mui/icons-material/SmartScreenOutlined';
9
import Gutters from '@/core/ui/grid/Gutters';
10
import { Actions } from '@/core/ui/actions/Actions';
11
import DialogHeader from '@/core/ui/dialog/DialogHeader';
12
import DialogWithGrid from '@/core/ui/dialog/DialogWithGrid';
13
import { MARKDOWN_TEXT_LENGTH } from '@/core/ui/forms/field-length.constants';
14
import { gutters } from '@/core/ui/grid/utils';
15
import { useConfig } from '@/domain/platform/config/useConfig';
16
import MarkdownInputToolbarButton, { MarkdownInputToolbarButtonProps } from '../MarkdownInputToolbarButton';
17

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

24
export const InsertEmbedCodeButton = ({
25
  editor,
×
26
  onDialogOpen,
27
  onDialogClose,
28
  ...buttonProps
29
}: InsertEmbedCodeButtonProps) => {
30
  const [isDialogOpen, setIsDialogOpen] = useState(false);
31

×
32
  const { t } = useTranslation();
33

×
34
  const notify = useNotification();
35

×
36
  const { integration: { iframeAllowedUrls = [] } = {} } = useConfig();
NEW
37

×
NEW
38
  const buttonRef = useRef<HTMLButtonElement>(null) as React.RefObject<HTMLButtonElement>;
×
39

40
  const textareaRef = useRef<HTMLTextAreaElement>(null);
×
41

42
  const handleOnCloseDialog = () => {
×
43
    setIsDialogOpen(false);
44
    editor?.commands.focus();
×
45
    onDialogClose?.();
×
46
  };
×
47

×
48
  const Textarea = styled(TextareaAutosize)(
49
    ({ theme }) => `
50
    box-sizing: border-box;
×
51
    width: 100%;
×
52
    max-width: 100%;
53
    min-width: 100%;
54
    font-family: monospace;
55
    font-size: 0.875rem;
56
    line-height: 1.5;
57
    padding: ${gutters(0.5)(theme)};
58
    border-radius: ${theme.shape.borderRadius}px ${theme.shape.borderRadius}px 0;
59
    color: ${theme.palette.grey[900]};
60
    background: ${theme.palette.background.default};
61
    border: 1px solid ${theme.palette.grey[200]};
62

63
    &:hover {
64
      border-color: ${theme.palette.grey[400]};
65
    }
66

67
    &:focus {
68
      outline: 0;
69
      border-color: ${theme.palette.grey[400]};
70
      box-shadow: 0 0 0 1px ${theme.palette.grey[200]};
71
    }
72

73
    /* firefox */
74
    &:focus-visible {
75
      outline: 0;
76
    }
77
  `
78
  );
79

80
  const handleOnClick = () => {
81
    setIsDialogOpen(true);
82
  };
×
83

×
84
  const handleOnSubmitIframe = ({ src }: { src: string }) => {
85
    const parser = new DOMParser();
86
    const doc = parser.parseFromString(src, 'text/html');
×
87
    const iframe = doc.querySelector('iframe');
×
88

×
89
    if (!iframe) {
×
90
      notify(t('components.wysiwyg-editor.embed.invalidOrUnsupportedEmbed'), 'error');
91
      return;
×
92
    }
×
93

×
94
    const embedCodeSrc = iframe.getAttribute('src');
95

96
    if (!embedCodeSrc) {
×
97
      notify(t('components.wysiwyg-editor.embed.invalidOrUnsupportedEmbed'), 'error');
98
      return;
×
99
    }
×
100

×
101
    const srcOrigin = new URL(embedCodeSrc).origin;
102
    const isValidSource = iframeAllowedUrls.some(vS => vS === srcOrigin);
103

×
NEW
104
    if (!isValidSource) {
×
105
      notify(t('components.wysiwyg-editor.embed.invalidOrUnsupportedEmbed'), 'error');
106
      return;
×
107
    }
×
108

×
109
    try {
110
      editor?.commands.setIframe({ src: embedCodeSrc });
111
    } catch (error) {
×
112
      if (error instanceof Error) {
×
113
        notify(error.message, 'error');
114
      }
×
115

×
116
      throw error;
117
    }
118

×
119
    handleOnCloseDialog();
120
  };
121

×
122
  useEffect(() => {
123
    setTimeout(() => {
124
      if (isDialogOpen) {
×
125
        textareaRef?.current?.focus();
×
126
      }
×
127
    }, 10);
×
128
  }, [textareaRef?.current, isDialogOpen]);
129

130
  const isDisabled = !editor || !editor.can().insertContent('');
131
  const initialValues = useMemo(() => ({ src: '' }), []);
132

×
133
  return (
×
134
    <>
135
      <MarkdownInputToolbarButton
×
136
        ref={buttonRef}
137
        disabled={isDisabled}
138
        onClick={handleOnClick}
139
        tooltip={t('components.wysiwyg-editor.toolbar.embed.video')}
140
        {...buttonProps}
141
      >
142
        <SmartScreenOutlinedIcon />
143
      </MarkdownInputToolbarButton>
144

145
      <DialogWithGrid
146
        open={isDialogOpen}
147
        onClose={handleOnCloseDialog}
148
        aria-labelledby="insert-embed-code-dialog-title"
149
      >
150
        <DialogHeader
151
          title={t('components.wysiwyg-editor.embed.dialogTitle')}
×
152
          onClose={handleOnCloseDialog}
153
          id="insert-embed-code-dialog-title"
154
        />
155
        <Formik initialValues={initialValues} onSubmit={handleOnSubmitIframe}>
156
          {({ setFieldValue, values }) => (
157
            <Form>
158
              <Gutters>
159
                <Textarea
×
160
                  ref={textareaRef}
161
                  value={values.src}
162
                  minRows={7}
163
                  maxLength={MARKDOWN_TEXT_LENGTH}
164
                  placeholder={t('components.wysiwyg-editor.embed.pasteEmbedCode')}
165
                  onChange={(event: ChangeEvent<HTMLTextAreaElement>) => setFieldValue('src', event.target.value)}
166
                  aria-label={t('components.wysiwyg-editor.embed.embedCodeTextAreaAriaLabel')}
167
                />
168

169
                <Actions justifyContent="space-between">
170
                  <Button onClick={handleOnCloseDialog}>{t('buttons.cancel')}</Button>
171

172
                  <Button type="submit" variant="contained">
173
                    {t('buttons.insert')}
174
                  </Button>
175
                </Actions>
176
              </Gutters>
177
            </Form>
178
          )}
179
        </Formik>
180
      </DialogWithGrid>
181
    </>
182
  );
183
};
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