Skip to content

Commit

Permalink
Merge pull request #426 from appfolio/update-data-pair
Browse files Browse the repository at this point in the history
Update data pair
  • Loading branch information
gthomas-appfolio authored Jun 25, 2018
2 parents fcebe49 + 9cfa304 commit 0fd8906
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 217 deletions.
25 changes: 16 additions & 9 deletions src/components/Datapair.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import PropTypes from 'prop-types';
import React from 'react';
import Col from './Col';
import Row from './Row';
import FormLabelGroup from './FormLabelGroup';
import Input from './Input';

const Datapair = props => (
<Row className="mb-1">
<Col xs={12} sm={4} className="text-sm-right text-secondary">{props.label}</Col>
<Col xs={12} sm={8}>{props.children || props.value}</Col>
</Row>
);
const Datapair = (props) => {
const { children, label, value, ...attributes } = props;
return (
<FormLabelGroup
inline
label={label}
rowClassName="mb-1"
{...attributes}
>
{children || <Input static>{value}</Input>}
</FormLabelGroup>
);
};

Datapair.propTypes = {
children: PropTypes.node,
label: PropTypes.node.isRequired,
value: PropTypes.string
value: PropTypes.node,
};

export default Datapair;
19 changes: 15 additions & 4 deletions src/components/FormLabelGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import Label from './Label';
import Required from './Required';
import Row from './Row';

const labelSizeTranslations = {
sm: 2,
md: 3,
lg: 4
};

// Note: `inline` has no effect at the moment. In reactstrap 5 and later, this property appears
// to be relevant for the <FormGroup>, though. As such, we may be able to simplify this class with
// the built-in support of inline forms in newer versions of reactstrap.
const FormLabelGroup = (props) => {
const {
children,
Expand All @@ -16,6 +25,7 @@ const FormLabelGroup = (props) => {
inputId,
hint,
label,
labelSize,
required,
rowClassName,
size,
Expand All @@ -24,9 +34,9 @@ const FormLabelGroup = (props) => {
} = props;

const rowColor = color || (feedback && 'danger');
const labelWidth = stacked ? 12 : 3;
const labelAlignment = stacked ? '' : 'text-sm-right';
const inputContainerWidth = (stacked || !label) ? 12 : 9;
const labelAlignment = stacked ? '' : 'text-sm-right pr-0';
const labelWidth = stacked ? 12 : (labelSizeTranslations[labelSize || 'md']);
const inputWidth = (stacked || !label) ? 12 : (12 - labelWidth);

return (
<FormGroup row className={rowClassName} color={rowColor}>
Expand All @@ -36,7 +46,7 @@ const FormLabelGroup = (props) => {
{required && label ? <Required /> : null}
</Label>
)}
<Col sm={inputContainerWidth}>
<Col sm={inputWidth}>
<Row>
<Col {...width}>
{children}
Expand All @@ -60,6 +70,7 @@ FormLabelGroup.propTypes = {
inputId: PropTypes.string,
hint: PropTypes.string,
label: PropTypes.node,
labelSize: PropTypes.oneOf([null, 'sm', 'md', 'lg']),
required: PropTypes.bool,
rowClassName: PropTypes.string,
size: PropTypes.string,
Expand Down
81 changes: 35 additions & 46 deletions src/components/FormRow.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import PropTypes from 'prop-types';
import React from 'react';
import CheckboxInput from './CheckboxInput';
import Col from './Col';
import FileInput from './FileInput';
import FormFeedback from './FormFeedback';
import FormGroup from './FormGroup';
import FormText from './FormText';
import FormLabelGroup from './FormLabelGroup';
import Input from './Input';
import Label from './Label';
import RadioInput from './RadioInput';
import Row from './Row';
import Required from './Required';
import StaticInput from './StaticInput';

const typeTranslations = {
Expand Down Expand Up @@ -41,6 +35,7 @@ const FormRow = (props) => {
id,
inline,
label,
labelSize,
required,
rowClassName,
size,
Expand All @@ -56,61 +51,56 @@ const FormRow = (props) => {
const [baseFeedback, childFeedback] = parseFeedback(feedback);
const rowColor = color || state || (baseFeedback && 'danger');

const labelWidth = stacked ? 12 : 3;
const labelAlignment = stacked ? '' : 'text-sm-right';

const inputContainerWidth = (stacked || !label) ? 12 : 9;

// TODO this should use FormLabelGroup vs duplicated code here:
return (
<FormGroup
row
className={rowClassName}
<FormLabelGroup
color={rowColor}
feedback={baseFeedback}
hint={hint}
inline={inline}
inputId={id}
label={label}
labelSize={labelSize}
required={required}
rowClassName={rowClassName}
size={size}
stacked={stacked}
width={width}
>
{label && (
<Label for={id} sm={labelWidth} size={size} className={labelAlignment}>
{label}
{required && label ? <Required /> : null}
</Label>
)}
<Col sm={inputContainerWidth}>
<Row>
<Col {...width}>
<InputElement
id={id}
size={size}
state={rowColor}
type={typeof type === 'string' ? type : null}
children={React.Children.map(children, child => React.cloneElement(child, { type }))}
{...attributes}
{...childFeedback}
/>
{hint && <FormText color="muted">{hint}</FormText>}
{baseFeedback && <FormFeedback>{baseFeedback}</FormFeedback>}
</Col>
</Row>
</Col>
</FormGroup>
<InputElement
id={id}
size={size}
state={rowColor}
type={typeof type === 'string' ? type : null}
children={React.Children.map(children, child => React.cloneElement(child, { type }))}
{...attributes}
{...childFeedback}
/>
</FormLabelGroup>
);
};

FormRow.propTypes = {
label: PropTypes.node,
hint: PropTypes.string,
children: PropTypes.node,
color: PropTypes.string,
feedback: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
]),
hint: PropTypes.string,
id: PropTypes.string,
inline: PropTypes.bool,
label: PropTypes.node,
labelSize: PropTypes.string,
required: PropTypes.bool,
rowClassName: PropTypes.string,
size: PropTypes.string,
stacked: PropTypes.bool,
state: PropTypes.string,
type: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
PropTypes.func
]),
inline: PropTypes.bool,
stacked: PropTypes.bool,
width: PropTypes.object
};

Expand All @@ -122,8 +112,7 @@ FormRow.defaultProps = {
required: false,
rowClassName: '',
stacked: false,
type: 'text',
width: { xs: 12 }
type: 'text'
};

export default FormRow;
16 changes: 10 additions & 6 deletions stories/Datapair.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ import { storiesOf } from '@storybook/react';

import { Datapair } from '../src';

const description = `
Datapairs are clear and concise elements for displaying labeled data. Each element is comprised
of two parts: the key (label) and a value. The key is an identifier for some form of data and
the value can be text or links.
`;

storiesOf('Datapair', module)
.addWithInfo('with props', () => (
.addWithInfo('with props', description, () => (
<Card className="mt-1">
<CardBlock>
<Datapair label="Key" value="Some simple content would go here" />
<Datapair label="Another Key" value="More content" />
</CardBlock>
</Card>
))
.addWithInfo('with HTML value', () => (
.addWithInfo('with HTML value', description, () => (
<Card className="mt-1">
<CardBlock>
<Datapair label="Label">
Expand All @@ -26,12 +32,10 @@ storiesOf('Datapair', module)
</CardBlock>
</Card>
))
.addWithInfo('with node in label', () => (
.addWithInfo('with node in label', description, () => (
<Card className="mt-1">
<CardBlock>
<Datapair label={<span>Name <Badge>awesome</Badge></span>}>
Stuff
</Datapair>
<Datapair label={<span>Name <Badge>awesome</Badge></span>} value="Stuff" />
</CardBlock>
</Card>
));
8 changes: 7 additions & 1 deletion stories/FormLabelGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { storiesOf } from '@storybook/react';
import { boolean, text, select } from '@storybook/addon-knobs';
import { Alert, FormLabelGroup } from '../src';

const description = `
Represents a form group consisting of a label + children, with built-in support for hint and
feedback.
`;

storiesOf('FormLabelGroup', module)
.addWithInfo('Live example', () => (
.addWithInfo('Live example', description, () => (
<div>
<FormLabelGroup
label={text('label', 'Some Input')}
labelSize={select('labelSize', ['sm', 'md', 'lg'], 'md')}
feedback={text('feedback', 'You must give a first name')}
color={select('color', ['', 'success', 'warning', 'danger'], 'danger')}
hint={text('hint', '')}
Expand Down
8 changes: 7 additions & 1 deletion stories/FormRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { storiesOf } from '@storybook/react';
import { boolean, text, select } from '@storybook/addon-knobs';
import { FormRow } from '../src';

const description = `
Represents a form group consisting of a label + input, with built-in support for hint and
feedback. If the input "type" property is not specified, then type "text" is used by default.
`;

storiesOf('FormRow', module)
.addWithInfo('Live example', () => (
.addWithInfo('Live example', description, () => (
<div>
<FormRow
label={text('label', 'First Name')}
labelSize={select('labelSize', ['sm', 'md', 'lg'], 'md')}
feedback={text('feedback', 'You must give a first name')}
color={select('color', ['', 'success', 'warning', 'danger'], 'danger')}
hint={text('hint', '')}
Expand Down
42 changes: 19 additions & 23 deletions test/components/Datapair.spec.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,36 @@
import React from 'react';
import assert from 'assert';
import { shallow } from 'enzyme';
import { Col, Datapair } from '../../src';
import { Datapair, FormLabelGroup, Input } from '../../src';

describe('<Datapair />', () => {
const component = shallow(<Datapair label="stuff" value="something" />);

it('should have a label and value', () => {
const cols = component.find(Col);
assert.equal(cols.length, 2);
assert.equal(cols.first().childAt(0).text(), 'stuff');
assert.equal(cols.last().childAt(0).text(), 'something');
});
it('should have a FormLabelGroup', () => {
const formLabelGroup = component.find(FormLabelGroup);
assert.equal(formLabelGroup.length, 1);
assert.equal(formLabelGroup.prop('label'), 'stuff');

it('should align and correctly size label column', () => {
const labelCol = component.find(Col).first();
assert.equal(labelCol.prop('sm'), '4');
assert.equal(labelCol.prop('xs'), '12');
assert(labelCol.hasClass('text-sm-right'));
const input = formLabelGroup.find(Input);
assert.equal(input.children().text(), 'something');
});

it('should correctly size value column', () => {
const valCol = component.find(Col).last();
assert.equal(valCol.prop('sm'), '8');
assert.equal(valCol.prop('xs'), '12');
it('should support html in label', () => {
const fancyComponent = shallow(<Datapair label={<span>stuff</span>} value="something" />);
const formLabelGroup = fancyComponent.find(FormLabelGroup);
assert.equal(shallow(formLabelGroup.prop('label')).html(), '<span>stuff</span>');
});

it('should support html in value', () => {
const fancyComponent = shallow(<Datapair label="stuff"><span>Special</span></Datapair>);
const valCol = fancyComponent.find(Col).last();
assert.equal(valCol.childAt(0).html(), '<span>Special</span>');
const fancyComponent = shallow(<Datapair label="stuff" value={<span>Special</span>} />);
const formLabelGroup = fancyComponent.find(FormLabelGroup);
const input = formLabelGroup.find(Input);
assert.equal(input.children().html(), '<span>Special</span>');
});

it('should support node in label', () => {
const fancyComponent = shallow(<Datapair label={<span>stuff</span>}>Special</Datapair>);
const valCol = fancyComponent.find(Col).first();
assert.equal(valCol.childAt(0).html(), '<span>stuff</span>');
it('should support children', () => {
const fancyComponent = shallow(<Datapair label="stuff"><span>Special</span></Datapair>);
const formLabelGroup = fancyComponent.find(FormLabelGroup);
assert.equal(formLabelGroup.children().html(), '<span>Special</span>');
});
});
Loading

0 comments on commit 0fd8906

Please sign in to comment.