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

microsoft / BotFramework-Composer / 9055280247

28 Mar 2024 01:12PM UTC coverage: 54.424% (-0.06%) from 54.482%
9055280247

push

github

web-flow
Merge pull request #9713 from OEvgeny/chore/update-electron-26

chore: update dependencies

7577 of 18338 branches covered (41.32%)

Branch coverage included in aggregate %.

939 of 1507 new or added lines in 170 files covered. (62.31%)

8 existing lines in 8 files now uncovered.

19745 of 31864 relevant lines covered (61.97%)

26.54 hits per line

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

70.0
/Composer/packages/lib/code-editor/src/lu/DefineEntityButton.tsx
1
// Copyright (c) Microsoft Corporation.
2
// Licensed under the MIT License.
3

4
import { FluentTheme } from '@fluentui/theme';
1✔
5
import formatMessage from 'format-message';
1✔
6
import { CommandBarButton as DefaultCommandBarButton } from '@fluentui/react/lib/Button';
1✔
7
import { Link } from '@fluentui/react/lib/Link';
1✔
8
import {
9
  ContextualMenuItemType,
10
  IContextualMenuItem,
11
  IContextualMenuProps,
12
  IContextualMenuItemProps,
13
  IContextualMenuItemRenderFunctions,
14
} from '@fluentui/react/lib/ContextualMenu';
1✔
15
import * as React from 'react';
12✔
16
import { LuFile } from '@bfc/shared';
17

18
import { ItemWithTooltip } from '../components/ItemWithTooltip';
1✔
19
import { useNoSearchResultMenuItem } from '../hooks/useNoSearchResultMenuItem';
1✔
20
import { useSearchableMenuListCallback } from '../hooks/useSearchableMenuListCallback';
1✔
21
import { withTooltip } from '../utils/withTooltip';
1✔
22
import { getEntityTypeDisplayName } from '../utils/luUtils';
1✔
23

24
import { jsLuToolbarMenuClassName, prebuiltEntities } from './constants';
1✔
25
import { getLuToolbarItemTextAndIcon } from './utils/iconUtils';
1✔
26
import { ToolbarLuEntityType, toolbarSupportedLuEntityTypes, ListEntity } from './types';
1✔
27
import { ListEntityCreationDialog } from './dialogs/ListEntityCreationDialog';
1✔
28
import { getEntityLuDefinition } from './utils/entityDefinition';
1✔
29
import { useLuEntities } from './hooks/useLuEntities';
1✔
30

31
const allowedLuEntityTypes: ToolbarLuEntityType[] = ['prebuilt', 'ml', 'list'];
1✔
32
const entityDefinitionLinkId = 'define-entity-menu-header-link';
1✔
33
const entityDefinitionHelpUrl =
34
  'https://docs.microsoft.com/en-us/azure/bot-service/file-format/bot-builder-lu-file-format?view=azure-bot-service-4.0#entity';
1✔
35

36
const fontSizeStyle = {
1✔
37
  fontSize: FluentTheme.fonts.small.fontSize,
38
};
39
const buttonStyles = {
1✔
40
  root: {
41
    height: 32,
42
    '&:hover .ms-Button-flexContainer i, &:active .ms-Button-flexContainer i, &.is-expanded .ms-Button-flexContainer i':
43
      {
44
        color: FluentTheme.palette.black,
45
      },
46
  },
47
  menuIcon: { fontSize: 8, color: FluentTheme.palette.black },
48
  label: { ...fontSizeStyle },
49
  icon: { color: FluentTheme.palette.black, fontSize: 12 },
50
};
51

52
const getCommandBarButton = (tooltipContent: string) =>
4✔
53
  withTooltip({ content: tooltipContent }, DefaultCommandBarButton);
54

55
type Props = {
56
  onDefineEntity: (
57
    entityType: ToolbarLuEntityType,
58
    data: Partial<{ entityName: string; entityDefinition: string }>,
59
  ) => void;
60
  disabled?: boolean;
61
  tooltip?: string;
62
  luFile?: LuFile;
63
};
64

65
export const DefineEntityButton = React.memo((props: Props) => {
1✔
66
  const { luFile, onDefineEntity, disabled = false, tooltip } = props;
12✔
67

68
  const entities = useLuEntities(luFile);
12✔
69

70
  const [showListEntityCreationDialog, setShowListEntityCreationDialog] = React.useState(false);
71
  const { iconName, text } = React.useMemo(() => getLuToolbarItemTextAndIcon('defineEntity'), []);
4✔
72
  const { onRenderMenuList, query, onReset } = useSearchableMenuListCallback(formatMessage('Search prebuilt entities'));
12✔
73

74
  const noSearchResultsMenuItem = useNoSearchResultMenuItem(formatMessage('no prebuilt entities found'));
12✔
75

76
  const onClickEntityType = React.useCallback(
77
    (entityType: ToolbarLuEntityType, ignoreEntityTypeList: ToolbarLuEntityType[] = [], entityName?: string) => {
×
78
      if (ignoreEntityTypeList.includes(entityType)) return;
2!
79

80
      switch (entityType) {
81
        case 'list':
3!
82
          setShowListEntityCreationDialog(true);
×
83
          break;
×
84
        case 'ml':
85
        case 'prebuilt':
86
          onDefineEntity(entityType, entityName ? { entityName } : {});
2✔
87
          break;
2✔
88
        default:
89
          throw `${entityType} is not supported!`;
×
90
      }
91
    },
92
    [onDefineEntity],
93
  );
94

95
  const filteredPrebuiltEntities = React.useMemo(() => {
96
    const filteredItems = query
6✔
97
      ? prebuiltEntities.filter((e) => e.toLowerCase().indexOf(query.toLowerCase()) !== -1)
16✔
98
      : prebuiltEntities;
99

100
    if (!filteredItems.length) {
6!
101
      return [noSearchResultsMenuItem];
×
102
    }
103

104
    return filteredItems.map<IContextualMenuItem>((prebuiltEntity) => ({
82✔
105
      key: prebuiltEntity,
106
      text: prebuiltEntity,
107
      style: fontSizeStyle,
108
      onClick: () => onClickEntityType('prebuilt', [], prebuiltEntity),
1✔
109
    }));
110
  }, [onDefineEntity, noSearchResultsMenuItem, query]);
111

112
  const prebuiltSubMenuProps = React.useMemo<IContextualMenuProps>(
113
    () => ({
6✔
114
      calloutProps: { calloutMaxHeight: 216 },
115
      items: filteredPrebuiltEntities,
116
      onRenderMenuList,
117
      onMenuDismissed: onReset,
118
    }),
119
    [filteredPrebuiltEntities, onReset, onRenderMenuList],
120
  );
121

122
  const renderMenuItemHeader = React.useCallback(
123
    (itemProps: IContextualMenuItemProps, defaultRenders: IContextualMenuItemRenderFunctions) => (
124
      <ItemWithTooltip
125
        aria-label={formatMessage('Learn more about {item}', { item: itemProps.item.text })}
126
        itemText={defaultRenders.renderItemName(itemProps)}
127
        tooltipId="define-entity-menu-header"
128
        tooltipText={formatMessage.rich('Visit <a>this page</a> to learn more about entity definition.', {
129
          a: ({ children }) => (
130
            <Link key={entityDefinitionLinkId} href={entityDefinitionHelpUrl} id={entityDefinitionLinkId}>
131
              {children}
132
            </Link>
133
          ),
134
        })}
135
      />
136
    ),
137
    [],
138
  );
139

140
  const menuItems = React.useMemo(() => {
141
    return [
6✔
142
      {
143
        key: 'defineEntity_header',
144
        itemType: ContextualMenuItemType.Header,
145
        text: formatMessage('Define new entity'),
146
        onRenderContent: renderMenuItemHeader,
147
      },
148
      ...toolbarSupportedLuEntityTypes
149
        .filter((t) => allowedLuEntityTypes.includes(t))
30✔
150
        .map((t) => ({
18✔
151
          key: `${t}Entity`,
152
          text: getEntityTypeDisplayName(t),
153
          style: fontSizeStyle,
154
          subMenuProps: t === 'prebuilt' ? prebuiltSubMenuProps : undefined,
18✔
155
          onClick: () => onClickEntityType(t, ['prebuilt']),
1✔
156
        })),
157
    ];
158
  }, [onDefineEntity, renderMenuItemHeader, prebuiltSubMenuProps]);
159

160
  const menuProps = React.useMemo(() => {
161
    return {
6✔
162
      items: menuItems,
163
      calloutProps: {
164
        preventDismissOnEvent: (
165
          e: Event | React.FocusEvent<Element> | React.KeyboardEvent<Element> | React.MouseEvent<Element, MouseEvent>,
166
        ) => {
167
          /**
168
           * Due to a bug in Fluent, tooltip in a button menu header dismisses when user clicks a link inside it
169
           * Here, it manually intercepts the event and opens the link to documentation
170
           */
171
          if (
×
172
            e.target instanceof HTMLElement &&
×
173
            (e.target as HTMLElement).tagName.toLowerCase() === 'a' &&
174
            (e.target as HTMLElement).id === entityDefinitionLinkId
175
          ) {
176
            window?.open((e.target as HTMLAnchorElement).href, '_blank');
×
177
            return true;
×
178
          }
179
          return false;
×
180
        },
181
      },
182
    };
183
  }, [menuItems]);
184

185
  const CommandBarButton = React.useMemo(
186
    () => getCommandBarButton(tooltip || formatMessage('Define new entity')),
4✔
187
    [tooltip],
188
  );
189

190
  const dismissListEntityCreationDialog = React.useCallback(() => {
191
    setShowListEntityCreationDialog(false);
×
192
  }, []);
193

194
  const createListEntity = React.useCallback(
195
    (listEntity: ListEntity) => {
196
      dismissListEntityCreationDialog();
×
197
      onDefineEntity('list', {
×
198
        entityName: listEntity.name,
199
        entityDefinition: getEntityLuDefinition(
200
          listEntity,
NEW
201
          entities.map((e) => e.Name),
×
202
        ),
203
      });
204
    },
205
    [onDefineEntity, dismissListEntityCreationDialog, entities],
206
  );
207

208
  return (
209
    <>
210
      <CommandBarButton
211
        className={jsLuToolbarMenuClassName}
212
        data-testid="menuButton"
213
        disabled={disabled}
214
        iconProps={{ iconName }}
215
        menuProps={menuProps}
216
        styles={buttonStyles}
217
      >
218
        {text}
219
      </CommandBarButton>
220
      {showListEntityCreationDialog && (
12✔
221
        <ListEntityCreationDialog onCreateListEntity={createListEntity} onDismiss={dismissListEntityCreationDialog} />
222
      )}
223
    </>
224
  );
225
});
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