Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/components/FieldLevelHelp/FieldLevelHelp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Icon } from '../Icon';
import { Button } from '../Button';
import { Popover } from '../Popover';
import { OverlayTrigger } from '../OverlayTrigger';

/**
* FieldLevelHelp Component for Patternfly React
*/
const FieldLevelHelp = ({ children, content, close, ...props }) => {
const trigger = 'click';
const overlay = <Popover id="popover">{content}</Popover>;
const rootClose = close === 'true';

return (
<OverlayTrigger
overlay={overlay}
placement={'top'}
trigger={trigger.split(' ')}
rootClose={rootClose}
>
<Button bsStyle="link">
<Icon type="pf" name={'info'} />
</Button>
</OverlayTrigger>
);
};

FieldLevelHelp.propTypes = {
/** additional fieldlevelhelp classes */
content: PropTypes.node,
/** leave popover/tooltip open */
close: PropTypes.string,
/** children nodes */
children: PropTypes.node
};
FieldLevelHelp.defaultProps = {
close: 'true'
};

export default FieldLevelHelp;
40 changes: 40 additions & 0 deletions src/components/FieldLevelHelp/FieldLevelHelp.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withKnobs, text, select } from '@storybook/addon-knobs';
import { defaultTemplate } from '../../../storybook/decorators/storyTemplates';
import { DOCUMENTATION_URL } from '../../../storybook/constants';
import { FieldLevelHelp } from './index';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should add it under the forms stories? it makes sense to me to see a complete form with field level help instead of a standalone?

@AparnaKarve AparnaKarve Jan 27, 2018 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 6c9f1d2, I have added the story under Forms.

It was a good call to add the FieldLevelHelp use case in the Form, since it looks like I may have stumbled upon a bug in the Popover (or OverlayTrigger) component.

When the Popover component is used in conjunction with the FormGroup component, the Formgroup component steals the focus from Popover, causing the popover to close immediately. This happens in a fraction of a second, giving a visual impression that the popover never opened.

Please see a demo of the above issue here -
under the Forms->Horizontal Form->Phone field

For this particular use case, a workaround would be to keep the Popover open - (An option that you requested here - #179 (comment)). That way, we would at least see the popover contents.
The 'Close Popover' knob demonstrates the above (set the value to false)

The other workaround is to use Tooltips - but since we are not considering tooltips for the FieldLevelHelp component, that is probably not an option.
The 'Popover/Tooltip' knob demonstrates the above (set the value to 'tooltip')

const stories = storiesOf('FieldLevelHelp', module);
stories.addDecorator(withKnobs);
stories.addDecorator(
defaultTemplate({
title: 'FieldLevelHelp',
documentationLink:
DOCUMENTATION_URL.PATTERNFLY_ORG_COMMUNICATION + 'inline-notifications/'
})
);

stories.addWithInfo('FieldLevelHelp', 'FieldLevelHelp', () => {
const close = select('Close Popover', ['true', 'false']);
const content = text(
'content',
'Enter the hostname in a valid format <br> <a target="_blank" href="http://www.test.example.com">Click here for examples of valid hostnames</a>'
);
const fieldLabel = text('Field Label', 'Hostname');

const htmlContent = (
<div
dangerouslySetInnerHTML={{
__html: content
}}
/>
);

return (
<div style={{ textAlign: 'center' }}>
{fieldLabel}
<FieldLevelHelp content={htmlContent} close={close} />
</div>
);
});
32 changes: 32 additions & 0 deletions src/components/FieldLevelHelp/FieldLevelHelp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* eslint-env jest */

import React from 'react';
import renderer from 'react-test-renderer';

import FieldLevelHelp from './FieldLevelHelp';

test('FieldLevelHelp renders properly', () => {
const component = renderer.create(
<FieldLevelHelp id="fieldlevelname1">Port Number</FieldLevelHelp>
);

let tree = component.toJSON();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you can use const here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2dd035d

expect(tree).toMatchSnapshot();
});

test('FieldLevelHelp allows to specify mode content and close', () => {
const component = renderer.create(
<div>
<label>Port Number</label>
<FieldLevelHelp
id="fieldlevelname1"
mode="popover"
content="Enter Port number between the 4000-5000 range"
close="true"
/>
</div>
);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`FieldLevelHelp allows to specify mode content and close 1`] = `
<div>
<label>
Port Number
</label>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
<span
aria-hidden="true"
className="pficon pficon-info"
/>
</button>
</div>
`;

exports[`FieldLevelHelp renders properly 1`] = `
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
<span
aria-hidden="true"
className="pficon pficon-info"
/>
</button>
`;
1 change: 1 addition & 0 deletions src/components/FieldLevelHelp/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as FieldLevelHelp } from './FieldLevelHelp';
21 changes: 19 additions & 2 deletions src/components/Form/Stories/BasicForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React from 'react';
import { action } from '@storybook/addon-actions';
import { select, boolean } from '@storybook/addon-knobs';
import { select, boolean, text } from '@storybook/addon-knobs';
import { Spinner } from '../../Spinner';
import { Button } from '../../Button';
import { Form } from '../index';
Expand Down Expand Up @@ -40,6 +40,18 @@ export const BasicFormFields = [
<Form.FormControl type="email" {...props} />
)
},
{
controlId: 'phone',
label: 'Phone',
useFieldLevelHelp: true,
content:
"Please specify Country code <br> <a target='_blank' href='https://countrycode.org/'>Click here for a list of Country codes</a>",
close: 'true',
help: 'Enter a valid phone number',
formControl: ({ validationState, ...props }) => (
<Form.FormControl type="phone" {...props} />
)
},
{
controlId: 'url',
label: 'My meeting URL',
Expand Down Expand Up @@ -90,5 +102,10 @@ export const getBasicFormKnobs = () => ({
]),
bsSize: select('Size', [null, 'small', 'large']),
showHelp: boolean('Show Help', true),
disabled: boolean('Disabled', false)
disabled: boolean('Disabled', false),
content: text(
'Field Level Help Content',
"Please specify Country code <br> <a target='_blank' href='https://countrycode.org/'>Click here for a list of Country codes</a>"
),
close: select('Close Popover', ['true', 'false'], 'true')
});
48 changes: 37 additions & 11 deletions src/components/Form/Stories/HorizontalFormField.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React from 'react';
import { Grid } from '../../Grid';
import { Form } from '../index';
import { FieldLevelHelp } from '../../FieldLevelHelp/index';

export const HorizontalFormField = ({
controlId,
Expand All @@ -12,6 +13,9 @@ export const HorizontalFormField = ({
validationState,
bsSize,
showHelp,
useFieldLevelHelp,
content,
close,
...props
}) => {
const controlProps = { ...props };
Expand All @@ -21,15 +25,37 @@ export const HorizontalFormField = ({

const formGroupProps = { key: controlId, controlId, ...controlProps };

return (
<Form.FormGroup {...formGroupProps}>
<Grid.Col componentClass={Form.ControlLabel} sm={3}>
{label}
</Grid.Col>
<Grid.Col sm={9}>
{formControl(controlProps)}
{showHelp && help && <Form.HelpBlock>{help}</Form.HelpBlock>}
</Grid.Col>
</Form.FormGroup>
);
if (useFieldLevelHelp) {
const htmlContent = (
<div
dangerouslySetInnerHTML={{
__html: content
}}
/>
);
return (
<Form.FormGroup {...formGroupProps}>
<Grid.Col componentClass={Form.ControlLabel} sm={3}>
{label}
<FieldLevelHelp content={htmlContent} close={close} />
</Grid.Col>
<Grid.Col sm={9}>
{formControl(controlProps)}
{showHelp && help && <Form.HelpBlock>{help}</Form.HelpBlock>}
</Grid.Col>
</Form.FormGroup>
);
} else {
return (
<Form.FormGroup {...formGroupProps}>
<Grid.Col componentClass={Form.ControlLabel} sm={3}>
{label}
</Grid.Col>
<Grid.Col sm={9}>
{formControl(controlProps)}
{showHelp && help && <Form.HelpBlock>{help}</Form.HelpBlock>}
</Grid.Col>
</Form.FormGroup>
);
}
};
12 changes: 12 additions & 0 deletions src/components/Form/Stories/SupportedControlsForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React from 'react';
import { select, boolean } from '@storybook/addon-knobs';
import { Form } from '../index';
import { FieldLevelHelp } from '../../FieldLevelHelp/index';

export const SupportedControlsFormFields = [
{
Expand Down Expand Up @@ -89,6 +90,17 @@ export const SupportedControlsFormFields = [
</div>
)
},
{
controlId: 'FieldLevelHelp',
label: 'Field Level Help',
help: 'Help text',
formControl: ({ validationState, ...props }) => (
<div>
Phone
<FieldLevelHelp {...props} content="More info here" inline />
</div>
)
},
{
controlId: 'textarea',
label: 'Textarea',
Expand Down
38 changes: 31 additions & 7 deletions src/components/Form/Stories/VerticalFormField.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React from 'react';
import { Form } from '../index';
import { FieldLevelHelp } from '../../FieldLevelHelp/index';

export const VerticalFormField = ({
controlId,
Expand All @@ -11,6 +12,9 @@ export const VerticalFormField = ({
validationState,
bsSize,
showHelp,
useFieldLevelHelp,
content,
close,
...props
}) => {
const controlProps = { ...props };
Expand All @@ -20,11 +24,31 @@ export const VerticalFormField = ({

const formGroupProps = { key: controlId, controlId, ...controlProps };

return (
<Form.FormGroup {...formGroupProps}>
{label && <Form.ControlLabel>{label}</Form.ControlLabel>}
{formControl(controlProps)}
{showHelp && help && <Form.HelpBlock>{help}</Form.HelpBlock>}
</Form.FormGroup>
);
if (useFieldLevelHelp) {
const htmlContent = (
<div
dangerouslySetInnerHTML={{
__html: content
}}
/>
);
return (
<Form.FormGroup {...formGroupProps}>
{label && <Form.ControlLabel>{label}</Form.ControlLabel>}
<Form.ControlLabel>
<FieldLevelHelp content={htmlContent} close={close} />
</Form.ControlLabel>
{formControl(controlProps)}
{showHelp && help && <Form.HelpBlock>{help}</Form.HelpBlock>}
</Form.FormGroup>
);
} else {
return (
<Form.FormGroup {...formGroupProps}>
{label && <Form.ControlLabel>{label}</Form.ControlLabel>}
{formControl(controlProps)}
{showHelp && help && <Form.HelpBlock>{help}</Form.HelpBlock>}
</Form.FormGroup>
);
}
};
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './components/Button';
export * from './components/Dropdown';
export * from './components/DropdownKebab';
export * from './components/EmptyState';
export * from './components/FieldLevelHelp';
export * from './components/Filter';
export * from './components/Form';
export * from './components/Grid';
Expand Down