Skip to content

Commit

Permalink
fix: [BD-46] fixed RTL direction styling in ProgressBar (#2641)
Browse files Browse the repository at this point in the history
  • Loading branch information
PKulkoRaccoonGang authored Sep 29, 2023
1 parent 105a200 commit b67f77c
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 23 deletions.
13 changes: 7 additions & 6 deletions src/ProgressBar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ProgressBarBase from 'react-bootstrap/ProgressBar';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Annotation from '../Annotation';
import { placeInfoAtZero } from './utils';
import { getOffsetStyles, placeInfoAtZero } from './utils';

export const ANNOTATION_CLASS = 'pgn__annotation';
const HINT_SWAP_PERCENT = 50;
Expand Down Expand Up @@ -38,11 +38,12 @@ function ProgressBarAnnotated({
const isThresholdHintAfter = threshold < HINT_SWAP_PERCENT;
const progressColor = VARIANTS.includes(variant) ? variant : PROGRESS_DEFAULT_VARIANT;
const thresholdColor = VARIANTS.includes(thresholdVariant) ? thresholdVariant : THRESHOLD_DEFAULT_VARIANT;
const direction = window.getComputedStyle(document.body).getPropertyValue('direction');

const positionAnnotations = useCallback(() => {
placeInfoAtZero(progressInfoRef, isProgressHintAfter, ANNOTATION_CLASS);
placeInfoAtZero(thresholdInfoRef, isThresholdHintAfter, ANNOTATION_CLASS);
}, [isProgressHintAfter, isThresholdHintAfter]);
placeInfoAtZero(progressInfoRef, direction, isProgressHintAfter, ANNOTATION_CLASS);
placeInfoAtZero(thresholdInfoRef, direction, isThresholdHintAfter, ANNOTATION_CLASS);
}, [direction, isProgressHintAfter, isThresholdHintAfter]);

useEffect(() => {
positionAnnotations();
Expand All @@ -65,7 +66,7 @@ function ProgressBarAnnotated({
{!!label && (
<div
className="pgn__progress-info"
style={{ left: `${now}%` }}
style={getOffsetStyles(now, direction)}
ref={progressInfoRef}
>
{!isProgressHintAfter && getHint(progressHint)}
Expand Down Expand Up @@ -96,7 +97,7 @@ function ProgressBarAnnotated({
{(!!threshold && !!thresholdLabel) && (
<div
className="pgn__progress-info"
style={{ left: `${threshold}%` }}
style={getOffsetStyles(threshold, direction)}
ref={thresholdInfoRef}
>
{!isThresholdHintAfter && getHint(thresholdHint)}
Expand Down
15 changes: 13 additions & 2 deletions src/ProgressBar/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
width: 100%;
position: relative;
overflow: visible;
padding: 3.125rem 0;

.progress {
overflow: visible;
Expand Down Expand Up @@ -58,6 +59,11 @@
height: $progress-threshold-circle;
border-radius: calc($progress-threshold-circle / 2);
z-index: 1;

[dir="rtl"] & {
left: -(calc($progress-threshold-circle / 2));
right: auto;
}
}
}
}
Expand All @@ -77,8 +83,13 @@
}

.pgn__progress-info {
display: inline-block;
position: relative;
position: absolute;
display: flex;
align-items: baseline;

&:first-child {
top: 0;
}
}

.pgn__progress-hint {
Expand Down
15 changes: 12 additions & 3 deletions src/ProgressBar/tests/ProgressBar.test.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import renderer from 'react-test-renderer';

import ProgressBar, { ANNOTATION_CLASS } from '..';
Expand Down Expand Up @@ -47,9 +48,8 @@ describe('<ProgressBar.Annotated />', () => {
expect(tree).toMatchSnapshot();
});
it('renders info blocks with calculated margins', () => {
const useReferenceSpy = jest.spyOn(React, 'useRef').mockReturnValue(ref);
mount(<ProgressBarElement />);
expect(useReferenceSpy).toHaveBeenCalledTimes(2);
jest.spyOn(React, 'useRef').mockReturnValue(ref);
render(<ProgressBarElement />);
expect(ref.current.style.marginLeft).not.toBeFalsy();
});
it('renders correct variant for progress bar and annotation', () => {
Expand Down Expand Up @@ -85,5 +85,14 @@ describe('<ProgressBar.Annotated />', () => {
expect(wrapper.find('.pgn__progress-info').get(0).props.children[2]).toEqual(false);
expect(wrapper.find('.pgn__progress-info').get(1).props.children[2]).toEqual(false);
});
it('should apply styles based on direction for threshold', () => {
window.getComputedStyle = jest.fn().mockReturnValue({ getPropertyValue: () => 'rtl' });
const { container } = render(<ProgressBarElement />);
const progressInfo = container.querySelector('.pgn__progress-info');
const computedStyles = window.getComputedStyle(progressInfo);

expect(computedStyles.getPropertyValue('directory')).toBe('rtl');
window.getComputedStyle.mockRestore();
});
});
});
30 changes: 24 additions & 6 deletions src/ProgressBar/tests/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,42 @@ describe('utils', () => {

expect(actualMarginLeft).toEqual(expectedMarginLeft);
});
it('correctly calculates left margin when annotationOnly equals to true', () => {
placeInfoAtZero(ref, false);
it('correctly calculates left margin when annotationOnly equals to true and dir equal ltr', () => {
placeInfoAtZero(ref, 'ltr', false);

const { children } = ref.current;
let marginLeft = 0.0;
let horizontalMargin = 0.0;
for (let i = 0; i < children.length || 0; i++) {
const elementParams = children[i].getBoundingClientRect();
if (children[i].className.includes(ANNOTATION_CLASS)) {
marginLeft += elementParams.width / 2;
horizontalMargin += elementParams.width / 2;
} else {
marginLeft += elementParams.width;
horizontalMargin += elementParams.width;
}
}
const expectedMarginLeft = `${-marginLeft}px`;
const expectedMarginLeft = `${-horizontalMargin}px`;
const actualMarginLeft = ref.current.style.marginLeft;

expect(actualMarginLeft).toEqual(expectedMarginLeft);
});
it('correctly calculates right margin when annotationOnly equals to true and dir equal rtl', () => {
placeInfoAtZero(ref, 'rtl', false);

const { children } = ref.current;
let horizontalMargin = 0.0;
for (let i = 0; i < children.length || 0; i++) {
const elementParams = children[i].getBoundingClientRect();
if (children[i].className.includes(ANNOTATION_CLASS)) {
horizontalMargin += elementParams.width / 2;
} else {
horizontalMargin += elementParams.width;
}
}
const expectedHorizontalMargin = `${-horizontalMargin}px`;
const actualMarginRight = ref.current.style.marginRight;

expect(actualMarginRight).toEqual(expectedHorizontalMargin);
});
it('returns false if reference is wrong', () => {
const wrongRef1 = {};
const wrongRef2 = { current: {} };
Expand Down
30 changes: 24 additions & 6 deletions src/ProgressBar/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,42 @@
* that the annotation pointer indicates on zero of the ProgressBar.
*
* @param {object} ref reference to the info block
* @param {string} direction direction of the page ("ltr" or "rtl")
* @param {boolean} annotationOnly ignores width of the hint
* @param {string} annotationClass is used to identify the annotation element
*/
// eslint-disable-next-line import/prefer-default-export
export const placeInfoAtZero = (ref, annotationOnly = true, annotationClass = 'pgn__annotation') => {
export const placeInfoAtZero = (
ref,
direction = 'ltr',
annotationOnly = true,
annotationClass = 'pgn__annotation',
) => {
if (!ref.current || !ref.current.style) { return false; }
const { children } = ref.current;
let marginLeft = 0.0;
let horizontalMargin = 0.0;

for (let i = 0; i < children.length || 0; i++) {
const elementParams = children[i].getBoundingClientRect();
if (children[i].className.includes(annotationClass)) {
marginLeft += elementParams.width / 2;
horizontalMargin += elementParams.width / 2;
} else {
marginLeft += annotationOnly ? 0.0 : elementParams.width;
horizontalMargin += annotationOnly ? 0.0 : elementParams.width;
}
}
// eslint-disable-next-line no-param-reassign
ref.current.style.marginLeft = `${-marginLeft}px`;
ref.current.style[direction === 'rtl' ? 'marginRight' : 'marginLeft'] = `${-horizontalMargin}px`;
return true;
};

/**
* Retrieves offset styles based on the page direction.
*
* @param {number} value - The offset value in percentages.
* @param {string} direction - The offset direction ('rtl' for rightward offset, 'ltr' for leftward offset).
* @returns {Object} An object containing offset styles with either the 'left' or 'right' property,
* depending on the direction.
*/
export const getOffsetStyles = (
value,
direction,
) => (direction === 'rtl' ? { right: `${value}%` } : { left: `${value}%` });

0 comments on commit b67f77c

Please sign in to comment.