Skip to content

Commit

Permalink
style: Updated Array UI (#1617)
Browse files Browse the repository at this point in the history
* Update validation and choice array ui

* Updated string array

* Updated regex pattern

* Updated array styles

* Style cleanup

* Fixed string array test

* Fixed ObjectArray tests

* Updated request header object

* Small change to custom object field

* Fixed styling

* Updated dialogs

* Fix margin issue

* Updated callback dependencies

* Added error message

* Updated callback dependencies

* Fixed margin issues

* Fixed styles

* Fixed transparent border issue

* Fixed border color
  • Loading branch information
tdurnford authored and cwhitten committed Dec 10, 2019
1 parent 1eeac22 commit 4180fed
Show file tree
Hide file tree
Showing 27 changed files with 819 additions and 332 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ describe('<StringArray />', () => {

it('can add an item', async () => {
const onAddClick = jest.fn();
const { findByText } = renderDefault({ canAdd: true, onAddClick });
const addBtn = await findByText('Add');
fireEvent.click(addBtn);
const { findByTestId } = renderDefault({ canAdd: true, onAddClick });
const input = await findByTestId('string-array-text-input');
fireEvent.change(input, { target: { value: 'test' } });
fireEvent.keyDown(input, { key: 'Enter', code: 13 });
expect(onAddClick).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

import { arrayItem, arrayItemField } from './styles';

const ArrayItem: React.FC<ArrayFieldItem> = props => {
const { hasMoveUp, hasMoveDown, hasRemove, onReorderClick, onDropIndexClick, index } = props;
const { children, hasMoveUp, hasMoveDown, hasRemove, onReorderClick, onDropIndexClick, index } = props;

// This needs to return true to dismiss the menu after a click.
const fabricMenuItemClickHandler = fn => e => {
Expand Down Expand Up @@ -42,17 +46,15 @@ const ArrayItem: React.FC<ArrayFieldItem> = props => {
];

return (
<div className="ArrayItem">
<div className="ArrayItemField">{props.children}</div>
<div className="ArrayItemContext">
<IconButton
menuProps={{ items: contextItems }}
menuIconProps={{ iconName: 'MoreVertical' }}
ariaLabel={formatMessage('Item Actions')}
data-testid="ArrayItemContextMenu"
styles={{ menuIcon: { color: NeutralColors.black, fontSize: FontSizes.size16 } }}
/>
</div>
<div css={arrayItem}>
<div css={arrayItemField}>{children}</div>
<IconButton
menuProps={{ items: contextItems }}
menuIconProps={{ iconName: 'MoreVertical' }}
ariaLabel={formatMessage('Item Actions')}
data-testid="ArrayItemContextMenu"
styles={{ menuIcon: { color: NeutralColors.black, fontSize: FontSizes.size16 } }}
/>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,146 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React from 'react';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
/** @jsx jsx */
import { jsx } from '@emotion/core';
import React, { useCallback, useState } from 'react';
import { ArrayFieldTemplateProps } from '@bfcomposer/react-jsonschema-form';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { FontSizes, NeutralColors, SharedColors } from '@uifabric/fluent-theme';
import { IconButton } from 'office-ui-fabric-react/lib/Button';
import { JSONSchema6 } from 'json-schema';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import formatMessage from 'format-message';

import { BaseField } from '../fields/BaseField';
import { WidgetLabel } from '../widgets/WidgetLabel';

import ArrayItem from './ArrayItem';
import {
arrayItemInputFieldContainer,
arrayItemField,
objectItemLabel,
objectItemInputField,
objectItemValueLabel,
} from './styles';

const ObjectArray: React.FunctionComponent<ArrayFieldTemplateProps> = props => {
const { items, canAdd, onAddClick } = props;
const { canAdd, idSchema, items, onAddClick, schema = {}, uiSchema = {} } = props;
const { object } = (uiSchema['ui:options'] || {}) as any;
const { items: itemSchema = {} } = schema;
const { properties = {} } = itemSchema as JSONSchema6;

const [value, setValue] = useState({});

const handleChange = useCallback(
property => (_, newValue?: string) => {
setValue(currentValue => ({ ...currentValue, [property]: newValue || '' }));
},
[setValue]
);

const handleKeyDown = useCallback(
event => {
if (event.key.toLowerCase() === 'enter') {
event.preventDefault();

if (Object.keys(value).length) {
onAddClick(event, value);
setValue({});
}
}
},
[onAddClick, setValue, value]
);

const isVisible = useCallback(
(property: string) => {
const { items: itemsSchema } = uiSchema;
return !(
itemsSchema['ui:hidden'] &&
Array.isArray(itemsSchema['ui:hidden']) &&
itemsSchema['ui:hidden'].includes(property)
);
},
[uiSchema]
);

return (
<BaseField {...props}>
{object && (
<div css={objectItemLabel}>
{Object.keys(properties)
.filter(isVisible)
.map((key, index) => {
const { description, title } = properties[key] as JSONSchema6;
const { __id = '' } = idSchema[key] || {};

return (
<div css={objectItemValueLabel} key={index}>
<WidgetLabel description={description} label={title} inline={true} id={__id} />
</div>
);
})}
<div style={{ width: '32px' }} />
</div>
)}
<div className="ObjectArray">
{items.map((element, idx) => (
<ArrayItem {...element} key={idx} />
))}
{canAdd && (
<DefaultButton
type="button"
onClick={onAddClick}
data-testid="ArrayContainerAdd"
styles={{ root: { marginTop: '14px' } }}
>
{formatMessage('Add')}
</DefaultButton>
)}
{canAdd &&
(!object ? (
<DefaultButton
type="button"
onClick={onAddClick}
data-testid="ArrayContainerAdd"
styles={{ root: { marginTop: '14px' } }}
>
{formatMessage('Add')}
</DefaultButton>
) : (
<div css={arrayItemInputFieldContainer}>
<div css={arrayItemField}>
{Object.keys(properties)
.filter(isVisible)
.map((property, index, items) => (
<div css={objectItemInputField} key={index}>
<TextField
autoComplete="off"
onChange={handleChange(property)}
onKeyDown={handleKeyDown}
placeholder={`${formatMessage('Add new')} ${property}`}
value={value[property] || ''}
iconProps={{
...(index === items.length - 1
? {
iconName: 'ReturnKey',
style: { color: SharedColors.cyanBlue10, opacity: 0.6 },
}
: {}),
}}
data-testid="object-array-text-input"
/>
</div>
))}
</div>
<IconButton
disabled={true}
menuIconProps={{ iconName: 'MoreVertical' }}
ariaLabel={formatMessage('Item Actions')}
styles={{
menuIcon: {
backgroundColor: NeutralColors.white,
color: NeutralColors.gray130,
fontSize: FontSizes.size16,
},
rootDisabled: {
backgroundColor: NeutralColors.white,
},
}}
/>
</div>
))}
</div>
</BaseField>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React from 'react';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
/** @jsx jsx */
import { jsx } from '@emotion/core';
import React, { useCallback, useState } from 'react';
import { ArrayFieldTemplateProps } from '@bfcomposer/react-jsonschema-form';
import { FontSizes, NeutralColors, SharedColors } from '@uifabric/fluent-theme';
import { IconButton } from 'office-ui-fabric-react/lib/Button';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import formatMessage from 'format-message';

import { BaseField } from '../fields/BaseField';

import { arrayItemField, arrayItemInputFieldContainer } from './styles';
import ArrayItem from './ArrayItem';

import './styles.css';

const StringArray: React.FunctionComponent<ArrayFieldTemplateProps> = props => {
const { canAdd, items, onAddClick } = props;
const [value, setValue] = useState('');

const handleChange = useCallback((_, newValue?: string) => setValue(newValue || ''), [setValue]);

const handleKeyDown = useCallback(
event => {
if (event.key.toLowerCase() === 'enter') {
event.preventDefault();

if (value) {
onAddClick(event, value);
setValue('');
}
}
},
[onAddClick, setValue, value]
);

return (
<BaseField {...props}>
{props.items.map((element, idx) => (
<ArrayItem {...element} key={idx} />
))}
{props.canAdd && (
<DefaultButton type="button" onClick={e => props.onAddClick(e)} styles={{ root: { marginTop: '10px' } }}>
{formatMessage('Add')}
</DefaultButton>
<div>
{items.map((element, idx) => (
<ArrayItem {...element} key={idx} />
))}
</div>
{canAdd && (
<div css={arrayItemInputFieldContainer}>
<div css={arrayItemField}>
<TextField
onChange={handleChange}
onKeyDown={handleKeyDown}
value={value}
iconProps={{
iconName: 'ReturnKey',
style: { color: SharedColors.cyanBlue10, opacity: 0.6 },
}}
data-testid="string-array-text-input"
/>
</div>
<IconButton
disabled={true}
menuIconProps={{ iconName: 'MoreVertical' }}
ariaLabel={formatMessage('Item Actions')}
styles={{
menuIcon: {
backgroundColor: NeutralColors.white,
color: NeutralColors.gray130,
fontSize: FontSizes.size16,
},
rootDisabled: {
backgroundColor: NeutralColors.white,
},
}}
/>
</div>
)}
</BaseField>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import ObjectArray from './ObjectArray';
import IDialogArray from './IDialogArray';

const ArrayFieldTemplate: React.FunctionComponent<ArrayFieldTemplateProps> = props => {
if (!props.schema.items) {
const { registry, schema } = props;

if (!schema.items) {
return null;
}

let itemSchema = props.schema.items as any;
let itemSchema = schema.items as any;
const $ref = itemSchema.$ref;

if (!itemSchema.type && $ref) {
itemSchema = findSchemaDefinition($ref, props.registry.definitions);
itemSchema = findSchemaDefinition($ref, registry.definitions);
}

switch (itemSchema.type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { css } from '@emotion/core';
import { NeutralColors } from '@uifabric/fluent-theme';

export const arrayItem = css`
display: flex;
padding: 7px 0;
border-bottom: 1px solid ${NeutralColors.gray30};
&:first-type-of {
border-top: 1px solid ${NeutralColors.gray30};
}
`;

export const arrayItemField = css`
flex: 1;
display: flex;
`;

export const arrayItemInputFieldContainer = css`
display: flex;
padding: 7px 0;
`;

export const objectItemLabel = css`
border-bottom: 1px solid ${NeutralColors.gray30};
display: flex;
`;

export const objectItemValueLabel = css`
color: ${NeutralColors.gray130};
flex: 1;
font-size: 14px;
margin-left: 7px;
& + & {
margin-left: 20px;
}
`;

export const objectItemInputField = css`
flex: 1;
& + & {
margin-left: 20px;
}
`;
Loading

0 comments on commit 4180fed

Please sign in to comment.