Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement highlightedtext component #1864

Merged
merged 8 commits into from
Oct 5, 2020
2 changes: 1 addition & 1 deletion library/styleguideComponents/ReactComponent/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
min-width: 330px;
}

.rsg--heading1-6{
.rsg--heading1-6 {
font-size: 1.2rem;
color: #061C3F;
}
64 changes: 64 additions & 0 deletions src/components/HighlightedText/__test__/HighlightedText.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

HighlightedText.spec.js change to highlightedText.spec.js

import { mount } from 'enzyme';
import styled from 'styled-components';
import HighlightedText from '../';

describe('<HighlightedText />', () => {
const parts = [
{
value: 'Apples',
type: 'text',
},
{
value: 'varieties',
type: 'hit',
},
{
value: 'Honeycrisp',
type: 'text',
},
];
it('should return 3 <span> with theirs respective values because the default wrapper is a span', () => {
const component = mount(<HighlightedText parts={parts} />);
const container = component.find('span');
expect(container.length).toBe(3);
parts.forEach(({ value }, index) => {
expect(
container
.at(index)
.html()
.includes(value),
).toBe(true);
});
});

it('it should not return a <p> container', () => {
const component = mount(<HighlightedText parts={parts} isInline />);
expect(component.html().includes('<p>')).toBe(false);
});

it('should return a custom <span> with id="hitText" in the container at(1) which corresponds to the text that has a type hit', () => {
const TextContainer = styled.span`
color: #808080;
`;

const HitContainer = styled.span`
color: #fff;
`;

// eslint-disable-next-line react/prop-types
const HitComponent = ({ children }) => {
return <HitContainer id="hitText">{children}</HitContainer>;
};

const component = mount(
<HighlightedText
parts={parts}
textComponent={TextContainer}
hitComponent={HitComponent}
/>,
);
const container = component.find('span');
expect(container.at(1).prop('id')).toBe('hitText');
});

Choose a reason for hiding this comment

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

we should change these tests to make clearer the difference between a hit and normal text

Copy link
Contributor

Choose a reason for hiding this comment

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

this second test you can delete the first the third test all the code

});
31 changes: 31 additions & 0 deletions src/components/HighlightedText/highlighContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import PropTypes, { bool, node } from 'prop-types';

export default function HighlighContainer({ children, isInline, className, style }) {
if (isInline) {
return (
<span className={className} style={style}>
{children}
</span>
);
}
return (
<p className={className} style={style}>
{children}
</p>
);
}

HighlighContainer.propTypes = {
className: PropTypes.string,
style: PropTypes.object,
children: node,
isInline: bool,
};

HighlighContainer.defaultProps = {
className: undefined,
style: undefined,
children: undefined,
isInline: false,
};
33 changes: 33 additions & 0 deletions src/components/HighlightedText/hitText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function HitText({
parts,
hitComponent: HitComponent,
textComponent: TextComponent,
}) {
return parts.map((part, index) => {
const key = `part-${index}`;
if (part.type === 'hit') {
return <HitComponent key={key}>{part.value}</HitComponent>;
}
return <TextComponent key={key}>{part.value}</TextComponent>;
});
}

HitText.propTypes = {
parts: PropTypes.arrayOf(
PropTypes.exact({
value: PropTypes.string,
type: PropTypes.string,
}),
),
hitComponent: PropTypes.elementType,
textComponent: PropTypes.elementType,
};

HitText.defaultProps = {
parts: undefined,
hitComponent: undefined,
textComponent: undefined,
};
16 changes: 16 additions & 0 deletions src/components/HighlightedText/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ComponentType } from 'react';
import { BaseProps } from '../types';

interface Part {
value?: string;
type?: string;
}

export interface HighlightedText extends BaseProps {
hitComponent?: ComponentType<{ children?: string }>;
textComponent?: ComponentType<{ children?: string }>;
parts?: Part[];
isInline: boolean;
LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
}

export default function(props: HighlightedText): JSX.Element | null;
60 changes: 60 additions & 0 deletions src/components/HighlightedText/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import PropTypes, { bool } from 'prop-types';
LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
import HitText from './hitText';
import HighlighContainer from './highlighContainer';
import { DefaultHitContainer, DefaultTextContainer } from './styled/index';

LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
/**
* HighlightedText is a component that highlights a part of a text.
*/

export default function HighlightedText(props) {
const { style, className, parts, hitComponent, textComponent, isInline } = props;
const finalHitContainer = hitComponent || DefaultHitContainer;
const finalTextContainer = textComponent || DefaultTextContainer;

return (
<HighlighContainer className={className} style={style} isInline={isInline}>
<HitText
parts={parts}
hitComponent={finalHitContainer}
textComponent={finalTextContainer}
/>
</HighlighContainer>
);
}

HighlightedText.propTypes = {
/** A CSS class for the outer element, in addition to the component's base classes. */
className: PropTypes.string,
/** An object with the custom styles of the container. */
style: PropTypes.object,
/** An array of objects with the text and the part to be highlighted */
parts: PropTypes.arrayOf(
PropTypes.exact({
value: PropTypes.string,
type: PropTypes.string,
}),
),
/**
* The component class or function that is going to be use to render
* the highlighted text
*/
hitComponent: PropTypes.elementType,
/**
* The component class or function that is going to be use to render
* the text not highlighted
*/
textComponent: PropTypes.elementType,
LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
/** A boolean that when it is true wraps the text in a span tag, and when it is false wraps it in a p tag, this lets you display the text block or inline */
LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
isInline: bool,
};

HighlightedText.defaultProps = {
className: undefined,
style: undefined,
parts: undefined,
hitComponent: undefined,
textComponent: undefined,
isInline: false,
};
80 changes: 80 additions & 0 deletions src/components/HighlightedText/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# HighlightedText base
##### This example shows the style that is applied by default to the component.
```js
import React from 'react';
import { HighlightedText } from 'react-rainbow-components';

const style = {
maxWidth: '700px',
textAlign: 'center',
padding: '20px',
margin: 'auto',
};

const parts = [
{
value: 'Apples come in several ',
type: 'text',
},
{
value: 'varieties',
type: 'hit',
},
{
value: ', including Fuji, Granny Smith, and Honeycrisp.',
type: 'text',
},
];

<HighlightedText parts={parts} style={style} />;

```

# HighlightedText with custom styles
##### This example shows the component when custom styles are applied to it.

```js
import React from 'react';
import styled from 'styled-components';
import { HighlightedText } from 'react-rainbow-components';

const style = {
maxWidth: '700px',
textAlign: 'center',
padding: '20px',
margin: 'auto',
};

const parts = [
{
value: 'Apples come in several ',
type: 'text',
},
{
value: 'varieties',
type: 'hit',
},
{
value: ', including Fuji, Granny Smith, and Honeycrisp.',
type: 'text',
},
];

const TextContainer = styled.span`
color: ${(props) => props.theme.rainbow.palette.text.title};
font-size: 1rem;
`;

const HitContainer = styled.span.attrs(props => {
return props.theme.rainbow.palette;
})
`
background-color: ${(props) => props.brand.main};
color: ${props => props.getContrastText(props.text.main)};
font-size: 1rem;
`;

<HighlightedText parts={parts} style={style} textComponent={TextContainer} hitComponent={HitContainer} />;

```

14 changes: 14 additions & 0 deletions src/components/HighlightedText/styled/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import styled from 'styled-components';
import attachThemeAttrs from '../../../styles/helpers/attachThemeAttrs';

export const DefaultHitContainer = attachThemeAttrs(styled.span)`
LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
color: ${props => props.palette.text.main};
font-weight: bold;
font-size: 1rem;
LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
font-family: 'Lato Black';
`;

export const DefaultTextContainer = attachThemeAttrs(styled.span)`
LeandroTorresSicilia marked this conversation as resolved.
Show resolved Hide resolved
color: ${props => props.palette.text.main};
font-size: 1rem;
`;
1 change: 1 addition & 0 deletions src/components/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export { default as FileSelector } from './FileSelector';
export { default as GMap } from './GMap';
export { default as GoogleAddressLookup } from './GoogleAddressLookup';
export { default as HelpText } from './HelpText';
export { default as HighlightedText } from './HighlightedText';
export { default as ImportRecordsFlow } from './ImportRecordsFlow';
export { default as Input } from './Input';
export { default as Lookup } from './Lookup';
Expand Down
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export { default as FileSelector } from './FileSelector';
export { default as GMap } from './GMap';
export { default as GoogleAddressLookup } from './GoogleAddressLookup';
export { default as HelpText } from './HelpText';
export { default as HighlightedText } from './HighlightedText';
yvmunayev marked this conversation as resolved.
Show resolved Hide resolved
export { default as ImportRecordsFlow } from './ImportRecordsFlow';
export { default as Input } from './Input';
export { default as Lookup } from './Lookup';
Expand Down