Skip to content

Commit

Permalink
feat: add Label component
Browse files Browse the repository at this point in the history
  • Loading branch information
Federico Zivolo committed Jan 3, 2019
1 parent 6b72dc9 commit 1ff7aec
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 2 deletions.
8 changes: 6 additions & 2 deletions packages/react-forms/src/InputToggle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ const Input = styled.input`

type Props = {
disabled?: boolean,
id?: string,
};

type State = {
Expand All @@ -128,11 +129,14 @@ class InputToggle extends React.Component<Props, State> {
<Container {...omit(this.props)(INPUT_ATTRIBUTES)}>
<Input
{...include(this.props)([...INPUT_ATTRIBUTES, 'disabled'])}
id={this.state.id}
id={this.props.id || this.state.id}
type="checkbox"
/>
<Slider disabled={isDisabled} />
<Handle disabled={isDisabled} htmlFor={this.state.id} />
<Handle
disabled={isDisabled}
htmlFor={this.props.id || this.state.id}
/>
</Container>
);
}
Expand Down
57 changes: 57 additions & 0 deletions packages/react-forms/src/Label/Label.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
The Label component allows to add an accompanying text to any form element.

By default, the label will render as a block element, taking the whole row:

```js
<>
<Label htmlFor="one">Label</Label>
<InputText id="one" />
</>
```

If the `inline` boolean property is provided, the element will render as an inline
element, which will make it lay near to any adjacent element:

```js
<>
<Label inline htmlFor="two">
Inline Label
</Label>

<InputText id="two" />
</>
```

If you desire to add a label to an existing form element without having to manually
add the needed spacings and alignments, you can pass a `renderControl` render-prop
to the Label component.

`renderControl` is a function that provides as unique argument a CSS class name that
contains the needed styling to properly space the form control and its label:

```js
<>
<Label
renderControl={controlClass => <InputToggle className={controlClass} />}
>
Wi-Fi
</Label>
<Label
renderControl={controlClass => <InputToggle className={controlClass} />}
>
Bluetooth
</Label>
</>
```

By default, the label will be right aligned, if you prefer it to stay on the left,
provide a `labelAlignment` property and set it to `left`:

```js
<Label
renderControl={controlClass => <InputText className={controlClass} />}
labelAlignment="left"
>
Name
</Label>
```
55 changes: 55 additions & 0 deletions packages/react-forms/src/Label/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders a label 1`] = `
.emotion-0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
height: 32px;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
<Label>
<label
className="emotion-0 emotion-1"
>
foobar
</label>
</Label>
`;

exports[`renders a left aligned label with form control 2`] = `
.emotion-1 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
height: 32px;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.emotion-0 {
margin-left: 10px;
}
<Label
labelAlignment="left"
renderControl={[Function]}
>
<label
className="emotion-1 emotion-2"
>
foobar
<div
className="emotion-0"
/>
</label>
</Label>
`;
52 changes: 52 additions & 0 deletions packages/react-forms/src/Label/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// @flow
import * as React from 'react';
import { css } from 'emotion';
import styled from '@emotion/styled/macro';

const HEIGHT = {
large: 50,
small: 24,
regular: 32,
};

const MARGIN = {
large: 15,
small: 5,
regular: 10,
};

type Props = {
renderControl: (controlClass: string) => React.Node,
labelAlignment?: 'left' | 'right',
children: React.Node,
size?: 'large' | 'small' | 'regular',
};

const genControlClass = ({ size, labelAlignment }) => css`
margin-${labelAlignment}: ${MARGIN[size]}px;
`;

const Label = styled(
({
renderControl,
labelAlignment = 'right',
children,
size = 'regular',
inline,
...props
}: Props) => (
<label {...props}>
{labelAlignment === 'left' && children}
{renderControl &&
renderControl(genControlClass({ labelAlignment, size }))}
{labelAlignment === 'right' && children}
</label>
)
)`
display: ${props => (props.inline ? 'inline-flex' : 'flex')};
height: ${props => HEIGHT[props.size || 'regular']}px;
align-items: center;
`;

// @component
export default Label;
42 changes: 42 additions & 0 deletions packages/react-forms/src/Label/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// @flow
import * as React from 'react';
import { mount } from 'enzyme';
import Label from '.';

it('renders a label', () => {
const wrapper = mount(<Label>foobar</Label>);
expect(wrapper).toMatchSnapshot();
});

it('renders an inline label', () => {
const wrapper = mount(<Label inline>foobar</Label>);
expect(wrapper).toHaveStyleRule('display', 'inline-flex');
});

it('renders a form control', () => {
mount(
<Label
renderControl={cn => {
expect(cn).toMatchInlineSnapshot(`"css-12frltk"`);
}}
>
foobar
</Label>
);
});

it('renders a left aligned label with form control', () => {
const wrapper = mount(
<Label
renderControl={cn => {
expect(cn).toMatchInlineSnapshot(`"css-17nfp2s"`);

return <div className={cn} />;
}}
labelAlignment="left"
>
foobar
</Label>
);
expect(wrapper).toMatchSnapshot();
});
1 change: 1 addition & 0 deletions packages/react-forms/src/utils/inputPropsFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const INPUT_ATTRIBUTES = [
'defaultValue',
'checked',
'form',
'id',
'list',
'min',
'max',
Expand Down

0 comments on commit 1ff7aec

Please sign in to comment.