Skip to content

Commit

Permalink
feat(Feedback): Add support for tooltips (#5189)
Browse files Browse the repository at this point in the history
* feat(Feedback): Add support for tooltips

* Renamed tooltip to feedbackTooltip in FormCheck and FormFile
  • Loading branch information
kyletsang committed May 19, 2020
1 parent f846dfb commit 59b1fce
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 4 deletions.
12 changes: 10 additions & 2 deletions src/Feedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,28 @@ const propTypes = {
* @type {('valid'|'invalid')}
*/
type: PropTypes.string.isRequired,

/** Display feedback as a tooltip. */
tooltip: PropTypes.bool,

as: PropTypes.elementType,
};

const defaultProps = {
type: 'valid',
tooltip: false,
};

const Feedback = React.forwardRef(
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
({ as: Component = 'div', className, type, ...props }, ref) => (
({ as: Component = 'div', className, type, tooltip, ...props }, ref) => (
<Component
{...props}
ref={ref}
className={classNames(className, type && `${type}-feedback`)}
className={classNames(
className,
`${type}-${tooltip ? 'tooltip' : 'feedback'}`,
)}
/>
),
);
Expand Down
10 changes: 9 additions & 1 deletion src/FormCheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ const propTypes = {
/** Manually style the input as invalid */
isInvalid: PropTypes.bool.isRequired,

/** Display feedback as a tooltip. */
feedbackTooltip: PropTypes.bool,

/** A message to display when the input is in a validation state */
feedback: PropTypes.node,
};
Expand All @@ -89,6 +92,7 @@ const defaultProps = {
disabled: false,
isValid: false,
isInvalid: false,
feedbackTooltip: false,
title: '',
};

Expand All @@ -102,6 +106,7 @@ const FormCheck = React.forwardRef(
disabled,
isValid,
isInvalid,
feedbackTooltip,
feedback,
className,
style,
Expand Down Expand Up @@ -165,7 +170,10 @@ const FormCheck = React.forwardRef(
<FormCheckLabel title={title}>{label}</FormCheckLabel>
)}
{(isValid || isInvalid) && (
<Feedback type={isValid ? 'valid' : 'invalid'}>
<Feedback
type={isValid ? 'valid' : 'invalid'}
tooltip={feedbackTooltip}
>
{feedback}
</Feedback>
)}
Expand Down
10 changes: 9 additions & 1 deletion src/FormFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ const propTypes = {
/** Manually style the input as invalid */
isInvalid: PropTypes.bool.isRequired,

/** Display feedback as a tooltip. */
feedbackTooltip: PropTypes.bool,

/** A message to display when the input is in a validation state */
feedback: PropTypes.node,

Expand Down Expand Up @@ -103,6 +106,7 @@ const defaultProps = {
disabled: false,
isValid: false,
isInvalid: false,
feedbackTooltip: false,
};

const FormFile = React.forwardRef(
Expand All @@ -114,6 +118,7 @@ const FormFile = React.forwardRef(
disabled,
isValid,
isInvalid,
feedbackTooltip,
feedback,
className,
style,
Expand Down Expand Up @@ -187,7 +192,10 @@ const FormFile = React.forwardRef(
</>
)}
{(isValid || isInvalid) && (
<Feedback type={isValid ? 'valid' : 'invalid'}>
<Feedback
type={isValid ? 'valid' : 'invalid'}
tooltip={feedbackTooltip}
>
{feedback}
</Feedback>
)}
Expand Down
16 changes: 16 additions & 0 deletions test/FeedbackSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,20 @@ describe('<Feedback>', () => {
it('Should have div as default component', () => {
mount(<Feedback />).assertSingle('div');
});

it('Should render valid feedback', () => {
mount(<Feedback type="valid" />).assertSingle('.valid-feedback');
});

it('Should render invalid feedback', () => {
mount(<Feedback type="invalid" />).assertSingle('.invalid-feedback');
});

it('Should render valid feedback tooltip', () => {
mount(<Feedback type="valid" tooltip />).assertSingle('.valid-tooltip');
});

it('Should render invalid feedback tooltip', () => {
mount(<Feedback type="invalid" tooltip />).assertSingle('.invalid-tooltip');
});
});
38 changes: 38 additions & 0 deletions test/FormCheckSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,42 @@ describe('<FormCheck>', () => {
const wrapper = mount(<FormCheck as={Surrogate} />);
wrapper.assertSingle('input.extraClass[type="checkbox"]');
});

it('Should render valid feedback properly', () => {
const wrapper = mount(<FormCheck label="My label" isValid />);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('valid');
expect(feedback.prop('tooltip')).to.be.false;
});

it('Should render invalid feedback properly', () => {
const wrapper = mount(
<FormCheck label="My label" isValid={false} isInvalid />,
);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('invalid');
expect(feedback.prop('tooltip')).to.be.false;
});

it('Should render valid feedback tooltip properly', () => {
const wrapper = mount(
<FormCheck label="My label" isValid feedbackTooltip />,
);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('valid');
expect(feedback.prop('tooltip')).to.be.true;
});

it('Should render invalid feedback tooltip properly', () => {
const wrapper = mount(
<FormCheck label="My label" isValid={false} isInvalid feedbackTooltip />,
);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('invalid');
expect(feedback.prop('tooltip')).to.be.true;
});
});
38 changes: 38 additions & 0 deletions test/FormFileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,42 @@ describe('<FormFile>', () => {
const wrapper = mount(<FormFile as={Surrogate} />);
wrapper.assertSingle('wrapper-element.extraClass');
});

it('Should render valid feedback properly', () => {
const wrapper = mount(<FormFile label="My label" isValid />);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('valid');
expect(feedback.prop('tooltip')).to.be.false;
});

it('Should render invalid feedback properly', () => {
const wrapper = mount(
<FormFile label="My label" isValid={false} isInvalid />,
);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('invalid');
expect(feedback.prop('tooltip')).to.be.false;
});

it('Should render valid feedback tooltip properly', () => {
const wrapper = mount(
<FormFile label="My label" isValid feedbackTooltip />,
);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('valid');
expect(feedback.prop('tooltip')).to.be.true;
});

it('Should render invalid feedback tooltip properly', () => {
const wrapper = mount(
<FormFile label="My label" isValid={false} isInvalid feedbackTooltip />,
);
const feedback = wrapper.find('Feedback');

expect(feedback.prop('type')).to.equal('invalid');
expect(feedback.prop('tooltip')).to.be.true;
});
});
1 change: 1 addition & 0 deletions types/components/Feedback.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BsPrefixComponent } from './helpers';
export interface FeedbackProps {
bsPrefix?: never;
type?: 'valid' | 'invalid';
tooltip?: boolean;
}

declare class Feedback<
Expand Down
1 change: 1 addition & 0 deletions types/components/FormCheck.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface FormCheckProps {
type?: 'checkbox' | 'radio' | 'switch';
isValid?: boolean;
isInvalid?: boolean;
feedbackTooltip?: boolean;
feedback?: React.ReactNode;
}

Expand Down
1 change: 1 addition & 0 deletions types/components/FormFile.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface FormFileProps {
custom?: boolean;
isValid?: boolean;
isInvalid?: boolean;
feedbackTooltip?: boolean;
feedback?: React.ReactNode;
lang?: string;
}
Expand Down
3 changes: 3 additions & 0 deletions types/simple.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ import {
</Col>
</Form.Group>
<Form.File id="custom-file" label="Custom file input" custom />
<Form.File id="file-tooltip" label="File tooltip" feedbackTooltip />
<Form.File
ref={React.createRef<HTMLInputElement & FormFile>()}
id="custom-file-ref"
Expand All @@ -242,6 +243,8 @@ import {
/>
<Form.File.Input />
<Form.Switch label="Switch" disabled />
<Form.Check id="check-tooltip" feedbackTooltip />
<FormControl.Feedback tooltip />
</Form>;

<div>
Expand Down
Loading

0 comments on commit 59b1fce

Please sign in to comment.