Skip to content

Commit

Permalink
Merge pull request #594 from catho/QTM-714
Browse files Browse the repository at this point in the history
feat(QTM-714): Migrated Tooltip component to css modules
  • Loading branch information
MarcosViniciusPC authored Oct 21, 2024
2 parents b69051b + ad6c0ce commit 6e55277
Show file tree
Hide file tree
Showing 10 changed files with 465 additions and 493 deletions.
1 change: 0 additions & 1 deletion components/Dropdown/Dropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ const Dropdown = ({
text={_buttonLabel}
hasLabel={hasLabel}
id={_id}
autocomplete={autocomplete}
skin={skin}
className={dropdownInputClass}
{...rest}
Expand Down
2 changes: 2 additions & 0 deletions components/Dropdown/__snapshots__/Dropdown.unit.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,7 @@ exports[`Dropdown component should match the snapshot 8`] = `
<input
aria-autocomplete="list"
aria-labelledby="downshift-7-label"
autocomplete="off"
class="TextInput-module__text-input___VCylJ Dropdown-module__dropdown-input___2Gm7H Dropdown-module__dropdown-input-autocomplete___6Jjs5"
id="dropdown-7"
placeholder="Select an option"
Expand Down Expand Up @@ -1949,6 +1950,7 @@ exports[`Dropdown component should match the snapshot with dark skin 8`] = `
<input
aria-autocomplete="list"
aria-labelledby="downshift-19-label"
autocomplete="off"
class="TextInput-module__text-input___VCylJ Dropdown-module__dropdown-input___2Gm7H Dropdown-module__dropdown-input-autocomplete___6Jjs5"
dark="skin"
id="dropdown-19"
Expand Down
104 changes: 21 additions & 83 deletions components/Tooltip/Tooltip.jsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,7 @@
import { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import placementConfig from './options';
import {
colors,
spacing,
baseFontSize as defaultBaseFontSize,
} from '../shared/theme';

const Tip = styled.div`
border-radius: 4px;
font-weight: bold;
opacity: ${({ visible }) => (visible ? '1' : '0')};
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
position: absolute;
line-height: 0;
text-align: center;
transition:
opacity 0.2s ease-in-out,
visibility 0.2s ease-in-out;
z-index: 100;
${({
theme: {
colors: { neutral },
spacing: { xsmall },
baseFontSize,
},
}) => `
background-color: ${neutral[700]};
border-color: ${neutral[700]};
color: ${neutral[100]};
font-size: ${baseFontSize}px;
padding: ${xsmall}px;
`}
${({ placement }) => placementConfig.tipPosition[placement]};
&:before {
content: '';
position: absolute;
${({ placement }) => placementConfig.arrowPosition[placement]};
}
`;

const TipText = styled.span`
display: inline-block;
max-width: ${({ multiline }) => (multiline ? 'unset' : '250px')};
overflow: hidden;
text-overflow: ellipsis;
white-space: ${({ multiline }) => (multiline ? 'pre' : 'nowrap')};
line-height: 20px;
text-align: ${({ multiline }) => (multiline ? 'left' : 'unset')};
`;

const Wrapper = styled.div`
position: relative;
float: left;
clear: left;
width: ${({ multiline }) => (multiline ? 'max-content' : 'unset')};
`;
import classNames from 'classnames';
import styles from './Tooltip.module.css';

class Tooltip extends Component {
constructor(props) {
Expand All @@ -73,37 +15,43 @@ class Tooltip extends Component {
render() {
const {
children,
className,
placement,
text,
visible: visibleProp,
theme,
multiline,
...rest
} = this.props;
const { visible: visibleState } = this.state;

const wrapperClass = classNames(
styles['tooltip-wrapper'],
{ [styles['tooltip-wrapper-multiline']]: multiline },
className,
);
const tipClass = classNames(styles.tip, styles[`tip-${placement}`], {
[styles['tip-visible']]: visibleProp || visibleState,
});
const tipTextClass = classNames(styles['tip-text'], {
[styles['tip-text-multiline']]: multiline,
});

return (
<Wrapper
<div
onMouseEnter={() => this.isVisible(true)}
onMouseLeave={() => this.isVisible(false)}
multiline={multiline}
className={wrapperClass}
{...rest}
>
<Tip
placement={placement}
visible={visibleProp || visibleState}
theme={theme}
>
<TipText multiline={multiline}>{text}</TipText>
</Tip>
<div className={tipClass}>
<span className={tipTextClass}>{text}</span>
</div>
{children}
</Wrapper>
</div>
);
}
}

Tip.displayName = 'Tip';

Tooltip.propTypes = {
/** Content the tooltip will show */
text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
Expand All @@ -115,22 +63,12 @@ Tooltip.propTypes = {
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
theme: PropTypes.shape({
spacing: PropTypes.object,
colors: PropTypes.object,
baseFontSize: PropTypes.number,
}),
};

Tooltip.defaultProps = {
placement: 'top',
visible: false,
multiline: false,
theme: {
spacing,
colors,
baseFontSize: defaultBaseFontSize,
},
};

export default Tooltip;
131 changes: 131 additions & 0 deletions components/Tooltip/Tooltip.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
:root {
--tip-arrow-size: 6px;
--tip-box-margin: 6px;
--tip-percentage-x: 50%;
--tip-percentage-y: 100%;
--tip-position-distance: calc(var(--tip-percentage-y) + var(--tip-arrow-size) + var(--tip-box-margin));
}

.tooltip-wrapper {
position: relative;
float: left;
clear: left;
width: unset;
}

.tooltip-wrapper-multiline {
width: max-content;
}

.tip {
border-radius: 4px;
font-weight: bold;
opacity: 0;
visibility: hidden;
position: absolute;
line-height: 0;
text-align: center;
transition:
opacity 0.2s ease-in-out,
visibility 0.2s ease-in-out;
z-index: 100;
background-color: var(--qtm-colors-neutral-700);
border-color: var(--qtm-colors-neutral-700);
color: var(--qtm-colors-neutral-100);
font-size: var(--qtm-base-font-size);
padding: var(--qtm-spacing-xsmall);
}

.tip-top {
left: var(--tip-percentage-x);
bottom: var(--tip-position-distance);
transform: translateX(calc(var(--tip-percentage-x) * -1));
}

.tip-bottom {
left: var(--tip-percentage-x);
top: var(--tip-position-distance);
transform: translateX(calc(var(--tip-percentage-x) * -1));
}

.tip-left {
right: var(--tip-position-distance);
top: var(--tip-percentage-x);
transform: translateY(calc(var(--tip-percentage-x) * -1));
}

.tip-right {
left: var(--tip-position-distance);
top: var(--tip-percentage-x);
transform: translateY(calc(var(--tip-percentage-x) * -1));
}

.tip:before {
content: '';
position: absolute;
}

.tip-top:before {
border-left: var(--tip-arrow-size) solid transparent;
border-right: var(--tip-arrow-size) solid transparent;
left: 50%;
transform: translateX(-50%);

border-top: var(--tip-arrow-size) solid;
border-top-color: inherit;
bottom: calc((var(--tip-arrow-size) - 1px) * -1);
}

.tip-bottom:before {
border-left: var(--tip-arrow-size) solid transparent;
border-right: var(--tip-arrow-size) solid transparent;
left: 50%;
transform: translateX(-50%);

border-bottom: var(--tip-arrow-size) solid;
border-bottom-color: inherit;
top: calc((var(--tip-arrow-size) - 1px) * -1);
}

.tip-right:before {
border-top: var(--tip-arrow-size) solid transparent;
border-bottom: var(--tip-arrow-size) solid transparent;
top: 50%;
transform: translateY(-50%);

border-right: var(--tip-arrow-size) solid;
border-right-color: inherit;
left: calc((var(--tip-arrow-size) - 1px) * -1);
}

.tip-left:before {
border-top: var(--tip-arrow-size) solid transparent;
border-bottom: var(--tip-arrow-size) solid transparent;
top: 50%;
transform: translateY(-50%);

border-left: var(--tip-arrow-size) solid;
border-left-color: inherit;
right: calc((var(--tip-arrow-size) - 1px) * -1);
}

.tip-visible {
opacity: 1;
visibility: visible;
}

.tip-text {
display: inline-block;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 20px;
text-align: unset;
}

.tip-text-multiline {
max-width: unset;
white-space: pre;
text-align: left;
}
13 changes: 7 additions & 6 deletions components/Tooltip/Tooltip.unit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,18 @@ describe('Tooltip component ', () => {
});

it('should toggle visibility when mouse enter and mouse leave', () => {
const classContainedInClassList = (expression, classList) =>
Array.from(classList).some((className) => expression.test(className));

render(<Tooltip text={TOOLTIP_TEXT}>{TOOLTIP_TRIGGER}</Tooltip>);

const tooltipTrigger = screen.getByText(TOOLTIP_TRIGGER);
fireEvent.mouseEnter(tooltipTrigger);
const tip = tooltipTrigger.firstChild;

const tip = screen.getByText(TOOLTIP_TEXT);
expect(classContainedInClassList(/visible/, tip.classList)).toBe(false);

expect(tip.parentNode).toHaveStyleRule('visibility', 'visible');

fireEvent.mouseLeave(tooltipTrigger);
fireEvent.mouseEnter(tooltipTrigger);

expect(tip.parentNode).toHaveStyleRule('visibility', 'hidden');
expect(classContainedInClassList(/visible/, tip.classList)).toBe(true);
});
});
Loading

0 comments on commit 6e55277

Please sign in to comment.