From 4327a32b9e807456652df31882617a6c62e078b8 Mon Sep 17 00:00:00 2001 From: Andy Brown Date: Mon, 29 Jun 2020 14:21:53 -0700 Subject: [PATCH 1/2] move AdaptiveForm components out of sub directory --- .../index.tsx => AdaptiveForm.tsx} | 6 ++-- .../src/components/AdaptiveForm/styles.ts | 28 ---------------- .../{AdaptiveForm => }/ErrorInfo.tsx | 0 .../{AdaptiveForm => }/FormTitle.tsx | 33 ++++++++++++++++--- 4 files changed, 31 insertions(+), 36 deletions(-) rename Composer/packages/extensions/adaptive-form/src/components/{AdaptiveForm/index.tsx => AdaptiveForm.tsx} (91%) delete mode 100644 Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/styles.ts rename Composer/packages/extensions/adaptive-form/src/components/{AdaptiveForm => }/ErrorInfo.tsx (100%) rename Composer/packages/extensions/adaptive-form/src/components/{AdaptiveForm => }/FormTitle.tsx (85%) diff --git a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/index.tsx b/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm.tsx similarity index 91% rename from Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/index.tsx rename to Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm.tsx index 01ce846e41..24ea26e9fb 100644 --- a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/index.tsx +++ b/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm.tsx @@ -5,10 +5,10 @@ import React, { useMemo } from 'react'; import { FormErrors, JSONSchema7, UIOptions, PluginConfig } from '@bfc/extension'; import ErrorBoundary from 'react-error-boundary'; -import PluginContext from '../../PluginContext'; -import { SchemaField } from '../SchemaField'; -import { mergePluginConfigs } from '../../utils/mergePluginConfigs'; +import PluginContext from '../PluginContext'; +import { mergePluginConfigs } from '../utils/mergePluginConfigs'; +import { SchemaField } from './SchemaField'; import FormTitle from './FormTitle'; import ErrorInfo from './ErrorInfo'; diff --git a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/styles.ts b/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/styles.ts deleted file mode 100644 index 4acab78ba8..0000000000 --- a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/styles.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -import { css } from '@emotion/core'; -import { FontSizes } from '@uifabric/styling'; - -export const title = { - container: css` - border-bottom: 1px solid #c8c6c4; - padding: 0 18px; - margin-bottom: 0px; - `, - - subtitle: css` - height: 15px; - line-height: 15px; - font-size: 12px; - font-weight: 600; - color: #4f4f4f; - margin: -7px 0 7px; - `, - - description: css` - margin-top: 0; - margin-bottom: 10px; - white-space: pre-line; - font-size: ${FontSizes.smallPlus}; - `, -}; diff --git a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/ErrorInfo.tsx b/Composer/packages/extensions/adaptive-form/src/components/ErrorInfo.tsx similarity index 100% rename from Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/ErrorInfo.tsx rename to Composer/packages/extensions/adaptive-form/src/components/ErrorInfo.tsx diff --git a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/FormTitle.tsx b/Composer/packages/extensions/adaptive-form/src/components/FormTitle.tsx similarity index 85% rename from Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/FormTitle.tsx rename to Composer/packages/extensions/adaptive-form/src/components/FormTitle.tsx index 313a7b3e00..e6f25d326f 100644 --- a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm/FormTitle.tsx +++ b/Composer/packages/extensions/adaptive-form/src/components/FormTitle.tsx @@ -8,11 +8,34 @@ import { FontWeights } from '@uifabric/styling'; import { FontSizes } from '@uifabric/fluent-theme'; import formatMessage from 'format-message'; import { UIOptions, JSONSchema7 } from '@bfc/extension'; - -import { EditableField } from '../fields/EditableField'; -import { Link } from '../Link'; - -import { title as styles } from './styles'; +import { css } from '@emotion/core'; + +import { EditableField } from './fields/EditableField'; +import { Link } from './Link'; + +export const styles = { + container: css` + border-bottom: 1px solid #c8c6c4; + padding: 0 18px; + margin-bottom: 0px; + `, + + subtitle: css` + height: 15px; + line-height: 15px; + font-size: 12px; + font-weight: 600; + color: #4f4f4f; + margin: -7px 0 7px; + `, + + description: css` + margin-top: 0; + margin-bottom: 10px; + white-space: pre-line; + font-size: ${FontSizes.size12}; + `, +}; interface FormTitleProps { description?: string; From ed48271a1538408b256cbad6e4f412a742494a57 Mon Sep 17 00:00:00 2001 From: Andy Brown Date: Mon, 29 Jun 2020 14:47:26 -0700 Subject: [PATCH 2/2] show loading spinner while waiting for form data and schema --- .../src/components/AdaptiveForm.tsx | 27 ++++++++--- .../src/components/LoadingTimeout.tsx | 47 +++++++++++++++++++ .../__tests__/LoadingTimeout.test.tsx | 42 +++++++++++++++++ 3 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 Composer/packages/extensions/adaptive-form/src/components/LoadingTimeout.tsx create mode 100644 Composer/packages/extensions/adaptive-form/src/components/__tests__/LoadingTimeout.test.tsx diff --git a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm.tsx b/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm.tsx index 24ea26e9fb..19af34d8b4 100644 --- a/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm.tsx +++ b/Composer/packages/extensions/adaptive-form/src/components/AdaptiveForm.tsx @@ -1,9 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +/** @jsx jsx */ +import { jsx } from '@emotion/core'; +import { css } from '@emotion/core'; import React, { useMemo } from 'react'; import { FormErrors, JSONSchema7, UIOptions, PluginConfig } from '@bfc/extension'; import ErrorBoundary from 'react-error-boundary'; +import formatMessage from 'format-message'; import PluginContext from '../PluginContext'; import { mergePluginConfigs } from '../utils/mergePluginConfigs'; @@ -11,6 +15,13 @@ import { mergePluginConfigs } from '../utils/mergePluginConfigs'; import { SchemaField } from './SchemaField'; import FormTitle from './FormTitle'; import ErrorInfo from './ErrorInfo'; +import { LoadingTimeout } from './LoadingTimeout'; + +const styles = { + errorLoading: css` + padding: 18px; + `, +}; export interface AdaptiveFormProps { errors?: string | FormErrors | string[] | FormErrors[]; @@ -29,12 +40,16 @@ export const AdaptiveForm: React.FC = function AdaptiveForm(p return pluginConfig || mergePluginConfigs(); }, [pluginConfig]); - if (!formData) { - return <>No Data; - } - - if (!schema) { - return null; + if (!formData || !schema) { + return ( + +
+ {formatMessage('{type} could not be loaded', { + type: formData ? formatMessage('Schema') : formatMessage('Dialog data'), + })} +
+
+ ); } return ( diff --git a/Composer/packages/extensions/adaptive-form/src/components/LoadingTimeout.tsx b/Composer/packages/extensions/adaptive-form/src/components/LoadingTimeout.tsx new file mode 100644 index 0000000000..2be3745128 --- /dev/null +++ b/Composer/packages/extensions/adaptive-form/src/components/LoadingTimeout.tsx @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** @jsx jsx */ +import { jsx } from '@emotion/core'; +import React, { useState, useEffect } from 'react'; +import { css } from '@emotion/core'; +import formatMessage from 'format-message'; +import { Spinner } from 'office-ui-fabric-react/lib/Spinner'; + +const container = css` + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +`; + +type LoadingTimeoutProps = { + timeout?: number; + children: React.ReactChild; +}; + +const LoadingTimeout: React.FC = (props) => { + const { children, timeout = 1000 } = props; + const [showFallback, setShowFallback] = useState(false); + + useEffect(() => { + const tId = setTimeout(() => { + setShowFallback(true); + }, timeout); + + return () => { + clearTimeout(tId); + }; + }, []); + + return showFallback ? ( +
{children}
+ ) : ( +
+ +
+ ); +}; + +export { LoadingTimeout }; diff --git a/Composer/packages/extensions/adaptive-form/src/components/__tests__/LoadingTimeout.test.tsx b/Composer/packages/extensions/adaptive-form/src/components/__tests__/LoadingTimeout.test.tsx new file mode 100644 index 0000000000..3ee810fbf4 --- /dev/null +++ b/Composer/packages/extensions/adaptive-form/src/components/__tests__/LoadingTimeout.test.tsx @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render, act } from '@bfc/test-utils'; +import assign from 'lodash/assign'; + +import { LoadingTimeout } from '../LoadingTimeout'; + +const defaultProps = { + timeout: 500, + children:
, +}; + +function renderSubject(overrides = {}) { + const { children, ...rest } = assign({}, defaultProps, overrides); + return render({children}); +} + +describe('', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + it('renders a spinner with "Loading"', () => { + const { container } = renderSubject(); + expect(container).toHaveTextContent('Loading'); + }); + + it('displays children after timeout', async () => { + const fallback =
Fallback content
; + const { container } = renderSubject({ children: fallback }); + expect(container).toHaveTextContent('Loading'); + + await act(async () => { + jest.advanceTimersByTime(499); + expect(container).toHaveTextContent('Loading'); + jest.advanceTimersByTime(2); + expect(container).toHaveTextContent('Fallback content'); + }); + }); +});