diff --git a/Composer/jest.config.js b/Composer/jest.config.js index b9d4e88ec9..539cd8b8fa 100644 --- a/Composer/jest.config.js +++ b/Composer/jest.config.js @@ -46,6 +46,7 @@ module.exports = { '/packages/lib/code-editor', '/packages/lib/shared', '/packages/lib/indexers', + '/packages/lib/ui-shared', '/packages/server', '/packages/electron-server', '/packages/tools/language-servers/language-generation', diff --git a/Composer/packages/client/src/components/Auth/AuthDialog.tsx b/Composer/packages/client/src/components/Auth/AuthDialog.tsx index 362830228d..085e6766bf 100644 --- a/Composer/packages/client/src/components/Auth/AuthDialog.tsx +++ b/Composer/packages/client/src/components/Auth/AuthDialog.tsx @@ -11,6 +11,8 @@ import { useCallback, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { FontWeights } from 'office-ui-fabric-react/lib/Styling'; import { FontSizes } from '@uifabric/fluent-theme'; +import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import { CopyableText } from '@bfc/ui-shared'; import { dispatcherState } from '../../recoilModel/atoms'; import { isTokenExpired } from '../../utils/auth'; @@ -59,6 +61,15 @@ export const AuthDialog: React.FC = (props) => { return true; }, [accessToken, graphToken]); + const renderLabel = (props, defaultRender) => { + return ( + + {defaultRender(props)} + + + ); + }; + return ( = (props) => { > { @@ -90,14 +102,14 @@ export const AuthDialog: React.FC = (props) => { setTokenError(''); } }} + onRenderLabel={renderLabel} /> {props.needGraph ? ( { @@ -108,6 +120,7 @@ export const AuthDialog: React.FC = (props) => { setGraphError(''); } }} + onRenderLabel={renderLabel} /> ) : null} diff --git a/Composer/packages/client/src/components/Notifications/TunnelingSetupNotification.tsx b/Composer/packages/client/src/components/Notifications/TunnelingSetupNotification.tsx index 6d3f0e7e87..7138769bf5 100644 --- a/Composer/packages/client/src/components/Notifications/TunnelingSetupNotification.tsx +++ b/Composer/packages/client/src/components/Notifications/TunnelingSetupNotification.tsx @@ -2,47 +2,30 @@ // Licensed under the MIT License. /** @jsx jsx */ -import { jsx, css } from '@emotion/core'; -import React from 'react'; +import { CopyableText } from '@bfc/ui-shared'; +import { css, jsx } from '@emotion/core'; +import { FontSizes } from '@uifabric/fluent-theme'; +import { FontWeights } from '@uifabric/styling'; import formatMessage from 'format-message'; -import { IconButton, IButtonStyles } from 'office-ui-fabric-react/lib/Button'; -import { NeutralColors, FontSizes, FluentTheme } from '@uifabric/fluent-theme'; import { Link } from 'office-ui-fabric-react/lib/Link'; -import { FontWeights } from '@uifabric/styling'; +import React from 'react'; -import { platform, OS } from '../../utils/os'; +import { OS, platform } from '../../utils/os'; import { CardProps } from './NotificationCard'; const container = css` - padding: 0 16px 16px 40px; - position: relative; -`; - -const commandContainer = css` - display: flex; - flex-flow: row nowrap; + padding: 0 8px 16px 12px; position: relative; - padding: 4px 28px 4px 8px; - background-color: ${NeutralColors.gray20}; - line-height: 22px; - margin: 1rem 0; `; -const copyContainer = css` +const header = css` margin: 0; margin-bottom: 4px; font-size: ${FontSizes.size16}; font-weight: ${FontWeights.semibold}; `; -const copyIconColor = FluentTheme.palette.themeDark; -const copyIconStyles: IButtonStyles = { - root: { position: 'absolute', right: 0, color: copyIconColor, height: '22px' }, - rootHovered: { backgroundColor: 'transparent', color: copyIconColor }, - rootPressed: { backgroundColor: 'transparent', color: copyIconColor }, -}; - const linkContainer = css` margin: 0; `; @@ -61,18 +44,9 @@ export const TunnelingSetupNotification: React.FC = (props) => { const port = data?.port; const command = `${getNgrok()} http ${port} --host-header=localhost`; - const copyLocationToClipboard = async () => { - try { - await window.navigator.clipboard.writeText(command); - } catch (e) { - // eslint-disable-next-line no-console - console.error('Something went wrong when trying to copy the command to clipboard.', e); - } - }; - return (
-

{title}

+

{title}

{formatMessage.rich('Install ngrok and run the following command to continue', { a: ({ children }) => ( @@ -82,16 +56,11 @@ export const TunnelingSetupNotification: React.FC = (props) => { ), })}

-
- {command} - -
+

{ + const { className, text, buttonAriaLabel = 'Copy text to clipboard', buttonTitle = 'Copy text to clipboard' } = props; + + const copyToClipboard = React.useCallback(async () => { + try { + await window.navigator.clipboard.writeText(text); + } catch (e) { + // eslint-disable-next-line no-console + console.error('Error: Copy text to clipboard. details: ', e); + } + }, [text]); + + return ( + + +

{text}
+ + + + + + ); +}; diff --git a/Composer/packages/lib/ui-shared/src/components/__tests__/CopyableText.test.tsx b/Composer/packages/lib/ui-shared/src/components/__tests__/CopyableText.test.tsx new file mode 100644 index 0000000000..b13eb12817 --- /dev/null +++ b/Composer/packages/lib/ui-shared/src/components/__tests__/CopyableText.test.tsx @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { render, fireEvent } from '@botframework-composer/test-utils'; +import React from 'react'; + +import { CopyableText } from '../CopyableText'; + +Object.assign(navigator, { + clipboard: { + writeText: () => {}, + }, +}); + +describe('', () => { + it('Should render the given text', async () => { + const textToCopy = 'This is a command'; + const { container } = render(); + + expect(container).toHaveTextContent(textToCopy); + }); + + it('Should copy text to clipboard when the copy button ios clicked', async () => { + jest.spyOn(navigator.clipboard, 'writeText'); + const textToCopy = 'This is a command'; + const { findByTestId } = render(); + + const copyButton = await findByTestId('copy-to-clipboard-button'); + fireEvent.click(copyButton); + + expect(navigator.clipboard.writeText).toHaveBeenCalledWith(textToCopy); + }); +}); diff --git a/Composer/packages/lib/ui-shared/src/components/index.ts b/Composer/packages/lib/ui-shared/src/components/index.ts index b2cd03068b..8df00fd0d9 100644 --- a/Composer/packages/lib/ui-shared/src/components/index.ts +++ b/Composer/packages/lib/ui-shared/src/components/index.ts @@ -11,3 +11,4 @@ export * from './ProvisionHandoff/ProvisionHandoff'; export * from './DisplayMarkdownDialog/DisplayMarkdownDialog'; export * from './tags/TagInput'; export * from './IconMenu'; +export * from './CopyableText';