Skip to content

Commit

Permalink
fix: various form bugs (#2592)
Browse files Browse the repository at this point in the history
* force number field to display blank value when no value set

* differentiate number and integer

* do not unset value if selecting the same type

* remove unecessary fragment

* make sure scroll bar appears correctly for form

* fix problem editing open object values

* remove dead styles

* fix ability to edit form title

* add missing aria labels

* send focus to key field after adding new value

* translate error messages

* do not default value to string

* use simplified margin syntax

* extract getValueType into utils

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
  • Loading branch information
a-b-r-o-w-n and cwhitten authored Apr 10, 2020
1 parent 59c9b31 commit 4f1ae4f
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 159 deletions.
14 changes: 6 additions & 8 deletions Composer/packages/client/src/pages/design/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -368,15 +368,13 @@ function DesignPage(props) {
<div css={contentWrapper}>
{match && <ToolBar toolbarItems={toolbarItems} />}
<Conversation css={editorContainer}>
<React.Fragment>
<div css={editorWrapper}>
<div css={visualPanel}>
{breadcrumbItems}
<VisualEditor openNewTriggerModal={openNewTriggerModal} />
</div>
<PropertyEditor />
<div css={editorWrapper}>
<div css={visualPanel}>
{breadcrumbItems}
<VisualEditor openNewTriggerModal={openNewTriggerModal} />
</div>
</React.Fragment>
<PropertyEditor />
</div>
</Conversation>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion Composer/packages/client/src/pages/design/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ export const formEditor = css`
flex: 1;
border: 0px;
transition: width 0.2s ease-in-out;
overflow: auto;
overflow-y: scroll;
height: 100%;
`;

export const breadcrumbClass = mergeStyleSets({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ interface FormTitleProps {
const FormTitle: React.FC<FormTitleProps> = props => {
const { name, description, schema, formData, uiOptions = {} } = props;

const handleTitleChange = (_e: React.FormEvent, newTitle?: string): void => {
const handleTitleChange = (newTitle?: string): void => {
if (props.onChange) {
props.onChange({
...formData.$designer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ const getFloat = (value: string, step: number) => {
return parseFloat(fixed);
};

export const NumberField: React.FC<FieldProps> = function NumberField(props) {
const NumberField: React.FC<FieldProps> = props => {
const { description, disabled, id, label, onChange, readonly, schema, value, uiOptions } = props;

const { type } = schema;

const updateValue = (step: number) => (value: string) => {
if (value === '') {
onChange(0);
return;
}

// if the number is a float, we need to convert to a fixed decimal place
// in order to avoid floating point math rounding errors (ex. 1.2000000001)
// ex. if step = 0.01, we fix to 2 decimals
Expand All @@ -32,17 +37,19 @@ export const NumberField: React.FC<FieldProps> = function NumberField(props) {
};

const step = type === 'integer' ? 1 : 0.1;
const displayValue = typeof value === 'number' ? value.toString() : '';

return (
<>
<FieldLabel description={description} id={id} label={label} helpLink={uiOptions?.helpLink} />
<SpinButton
id={id}
disabled={Boolean(schema.const) || readonly || disabled}
step={step}
styles={{
labelWrapper: { display: 'none' },
}}
value={value}
value={displayValue}
onDecrement={updateValue(-step)}
onIncrement={updateValue(step)}
onValidate={updateValue(0)}
Expand All @@ -53,3 +60,5 @@ export const NumberField: React.FC<FieldProps> = function NumberField(props) {
</>
);
};

export { NumberField };
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Dropdown, IDropdownOption, ResponsiveMode } from 'office-ui-fabric-reac
import formatMessage from 'format-message';

import { FieldLabel } from '../FieldLabel';
import { resolveRef, resolveFieldWidget } from '../../utils';
import { resolveRef, resolveFieldWidget, getValueType } from '../../utils';
import { usePluginConfig } from '../../hooks';

import { oneOfField } from './styles';
Expand Down Expand Up @@ -53,7 +53,7 @@ const getSelectedOption = (value: any | undefined, options: IDropdownOption[]):
return;
}

const valueType = Array.isArray(value) ? 'array' : typeof value;
const valueType = getValueType(value);

if (valueType === 'array') {
const item = value[0];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/** @jsx jsx */
import { jsx } from '@emotion/core';
import React, { useState, useCallback, useMemo } from 'react';
import { FontSizes, NeutralColors } from '@uifabric/fluent-theme';
import { IconButton } from 'office-ui-fabric-react/lib/Button';
import { IContextualMenuItem } from 'office-ui-fabric-react/lib/ContextualMenu';
import formatMessage from 'format-message';

import { EditableField } from '../EditableField';

import { container, item } from './styles';

interface ObjectItemProps {
name: string;
formData: any;
value: any;
onNameChange: (name: string) => void;
onValueChange: (value?: string) => void;
onDelete: () => void;
}

const ObjectItem: React.FC<ObjectItemProps> = ({
name: originalName,
formData,
value,
onNameChange,
onValueChange,
onDelete,
}) => {
const initialName = useMemo(() => originalName, []);
const initialValue = useMemo(() => value, []);
const [name, setName] = useState<string>(originalName);
const [errorMessage, setErrorMessage] = useState<string>('');

const contextItems: IContextualMenuItem[] = [
{
iconProps: { iconName: 'Cancel' },
key: 'remove',
onClick: onDelete,
text: 'Remove',
},
];

const handleBlur = useCallback(() => {
if (!name || name === '') {
setErrorMessage(formatMessage('Key cannot be blank'));
} else if (name !== originalName && Object.keys(formData).includes(name)) {
setErrorMessage(formatMessage('Keys must be unique'));
} else {
onNameChange(name);
setErrorMessage('');
}
}, [name, formData]);

return (
<div css={container}>
<div css={item}>
<EditableField
transparentBorder
depth={0}
error={errorMessage}
id={`${name}.key`}
name="key"
placeholder={initialName || formatMessage('Add a new key')}
schema={{}}
styles={{
errorMessage: { display: 'block', paddingTop: 0 },
root: { margin: '7px 0' },
}}
uiOptions={{}}
value={name}
onBlur={handleBlur}
onChange={newValue => setName(newValue || '')}
ariaLabel={formatMessage('key')}
/>
</div>
<div css={item}>
<EditableField
transparentBorder
depth={0}
id={`${name}.value`}
name="value"
placeholder={initialValue || formatMessage('Add a new value')}
schema={{}}
styles={{
root: { margin: '7px 0' },
}}
uiOptions={{}}
value={value}
onChange={onValueChange}
ariaLabel={formatMessage('value')}
/>
</div>
<IconButton
ariaLabel={formatMessage('Edit Property')}
menuIconProps={{ iconName: 'MoreVertical' }}
menuProps={{ items: contextItems }}
styles={{
root: { margin: '7px 0 7px 0' },
menuIcon: { color: NeutralColors.black, fontSize: FontSizes.size16 },
}}
/>
</div>
);
};

export { ObjectItem };
Loading

0 comments on commit 4f1ae4f

Please sign in to comment.