diff --git a/packages/react-core/src/components/Drawer/DrawerPanelContent.tsx b/packages/react-core/src/components/Drawer/DrawerPanelContent.tsx index 522c7da2ea4..d2651622d9a 100644 --- a/packages/react-core/src/components/Drawer/DrawerPanelContent.tsx +++ b/packages/react-core/src/components/Drawer/DrawerPanelContent.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import styles from '@patternfly/react-styles/css/components/Drawer/drawer'; import { css } from '@patternfly/react-styles'; import { DrawerColorVariant, DrawerContext } from './Drawer'; -import { formatBreakpointMods } from '../../helpers/util'; +import { formatBreakpointMods, getLanguageDirection } from '../../helpers/util'; import { GenerateId } from '../../helpers/GenerateId/GenerateId'; import { FocusTrap } from '../../helpers/FocusTrap/FocusTrap'; import cssPanelMdFlexBasis from '@patternfly/react-tokens/dist/esm/c_drawer__panel_md_FlexBasis'; @@ -111,7 +111,7 @@ export const DrawerPanelContent: React.FunctionComponent { let splitterPos; let drawerSize; - const isRTL = window.getComputedStyle(panel.current).getPropertyValue('direction') === 'rtl'; + const isRTL = getLanguageDirection(panel.current) === 'rtl'; if (isInline && (position === 'end' || position === 'right')) { if (isRTL) { @@ -198,7 +198,7 @@ export const DrawerPanelContent: React.FunctionComponent { - const isRTL = window.getComputedStyle(panel.current).getPropertyValue('direction') === 'rtl'; + const isRTL = getLanguageDirection(panel.current) === 'rtl'; e.stopPropagation(); if (!isResizing) { @@ -264,7 +264,7 @@ export const DrawerPanelContent: React.FunctionComponent { - const isRTL = window.getComputedStyle(panel.current).getPropertyValue('direction') === 'rtl'; + const isRTL = getLanguageDirection(panel.current) === 'rtl'; const key = e.key; if ( diff --git a/packages/react-core/src/components/Nav/NavList.tsx b/packages/react-core/src/components/Nav/NavList.tsx index 27ee81f9ba3..60f7dd63174 100644 --- a/packages/react-core/src/components/Nav/NavList.tsx +++ b/packages/react-core/src/components/Nav/NavList.tsx @@ -3,7 +3,7 @@ import styles from '@patternfly/react-styles/css/components/Nav/nav'; import { css } from '@patternfly/react-styles'; import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon'; import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon'; -import { isElementInView } from '../../helpers/util'; +import { getLanguageDirection, isElementInView } from '../../helpers/util'; import { NavContext } from './Nav'; import { PageSidebarContext } from '../Page/PageSidebar'; import { getResizeObserver } from '../../helpers/resizeObserver'; @@ -112,7 +112,7 @@ class NavList extends React.Component { componentDidMount() { this.observer = getResizeObserver(this.navList.current, this.handleScrollButtons, true); - this.direction = getComputedStyle(this.navList.current).getPropertyValue('direction'); + this.direction = getLanguageDirection(this.navList.current); this.handleScrollButtons(); } @@ -121,7 +121,7 @@ class NavList extends React.Component { } componentDidUpdate() { - this.direction = getComputedStyle(this.navList.current).getPropertyValue('direction'); + this.direction = getLanguageDirection(this.navList.current); } render() { diff --git a/packages/react-core/src/components/Slider/Slider.tsx b/packages/react-core/src/components/Slider/Slider.tsx index 0b97002e26b..654a30276a3 100644 --- a/packages/react-core/src/components/Slider/Slider.tsx +++ b/packages/react-core/src/components/Slider/Slider.tsx @@ -8,6 +8,7 @@ import { TextInput } from '../TextInput'; import { Tooltip } from '../Tooltip'; import cssSliderValue from '@patternfly/react-tokens/dist/esm/c_slider_value'; import cssFormControlWidthChars from '@patternfly/react-tokens/dist/esm/c_slider__value_c_form_control_width_chars'; +import { getLanguageDirection } from '../../helpers/util'; /** Properties for creating custom steps in a slider. These properties should be passed in as * an object within an array to the slider component's customSteps property. @@ -125,7 +126,7 @@ export const Slider: React.FunctionComponent = ({ let isRTL: boolean; React.useEffect(() => { - isRTL = window.getComputedStyle(sliderRailRef.current).getPropertyValue('direction') === 'rtl'; + isRTL = getLanguageDirection(sliderRailRef.current) === 'rtl'; }); React.useEffect(() => { diff --git a/packages/react-core/src/components/Tabs/Tabs.tsx b/packages/react-core/src/components/Tabs/Tabs.tsx index ea37e047675..7ec05827a3a 100644 --- a/packages/react-core/src/components/Tabs/Tabs.tsx +++ b/packages/react-core/src/components/Tabs/Tabs.tsx @@ -6,7 +6,7 @@ import { PickOptional } from '../../helpers/typeUtils'; import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon'; import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon'; import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon'; -import { getUniqueId, isElementInView, formatBreakpointMods } from '../../helpers/util'; +import { getUniqueId, isElementInView, formatBreakpointMods, getLanguageDirection } from '../../helpers/util'; import { TabContent } from './TabContent'; import { TabProps } from './Tab'; import { TabsContextProvider } from './TabsContext'; @@ -333,7 +333,7 @@ class Tabs extends React.Component { if (canUseDOM) { window.addEventListener('resize', this.handleScrollButtons, false); } - this.direction = getComputedStyle(this.tabList.current).getPropertyValue('direction'); + this.direction = getLanguageDirection(this.tabList.current); // call the handle resize function to check if scroll buttons should be shown this.handleScrollButtons(); } @@ -381,7 +381,7 @@ class Tabs extends React.Component { this.setState({ showScrollButtons: false }); } - this.direction = getComputedStyle(this.tabList.current).getPropertyValue('direction'); + this.direction = getLanguageDirection(this.tabList.current); } render() { diff --git a/packages/react-core/src/helpers/Popper/Popper.tsx b/packages/react-core/src/helpers/Popper/Popper.tsx index bda6a65a496..fb073270ef7 100644 --- a/packages/react-core/src/helpers/Popper/Popper.tsx +++ b/packages/react-core/src/helpers/Popper/Popper.tsx @@ -5,6 +5,7 @@ import { Placement, Modifier } from './thirdparty/popper-core'; import { clearTimeouts } from '../util'; import { css } from '@patternfly/react-styles'; import '@patternfly/react-styles/css/components/Popper/Popper.css'; +import { getLanguageDirection } from '../util'; const hash = { left: 'right', @@ -45,21 +46,6 @@ const getOppositePlacement = (placement: Placement): any => export const getOpacityTransition = (animationDuration: number) => `opacity ${animationDuration}ms cubic-bezier(.54, 1.5, .38, 1.11)`; -export const getLanguageDirection = (targetElement: HTMLElement) => { - const defaultDirection = 'ltr'; - let direction = defaultDirection; - - if (targetElement) { - direction = getComputedStyle(targetElement).getPropertyValue('direction'); - } - - if (['ltr', 'rtl'].includes(direction)) { - return direction as 'ltr' | 'rtl'; - } - - return defaultDirection; -}; - export interface PopperProps { /** * Trigger reference element to which the popper is relatively placed to. diff --git a/packages/react-core/src/helpers/__mocks__/util.ts b/packages/react-core/src/helpers/__mocks__/util.ts index e6b3036138a..5be1b4a6e2c 100644 --- a/packages/react-core/src/helpers/__mocks__/util.ts +++ b/packages/react-core/src/helpers/__mocks__/util.ts @@ -1,3 +1,5 @@ export const getUniqueId = () => 'unique_id_mock'; export const clearTimeouts = () => {}; + +export const getLanguageDirection = () => 'ltr'; diff --git a/packages/react-core/src/helpers/util.ts b/packages/react-core/src/helpers/util.ts index 6c127df494f..88ba6a266ca 100644 --- a/packages/react-core/src/helpers/util.ts +++ b/packages/react-core/src/helpers/util.ts @@ -522,3 +522,25 @@ export const clearTimeouts = (timeoutRefs: React.RefObject[]) => { } }); }; + +/** + * Helper function to get the language direction of a given element, useful for figuring out if left-to-right + * or right-to-left specific logic should be applied. + * + * @param {HTMLElement} targetElement - Element the helper will get the language direction of + * @param {'ltr' | 'rtl'} defaultDirection - Language direction to assume if one can't be determined, defaults to 'ltr' + * @returns {'ltr' | 'rtl'} - The language direction of the target element + */ +export const getLanguageDirection = (targetElement: HTMLElement, defaultDirection: 'ltr' | 'rtl' = 'ltr') => { + if (!targetElement) { + return defaultDirection; + } + + const computedDirection = getComputedStyle(targetElement).getPropertyValue('direction'); + + if (['ltr', 'rtl'].includes(computedDirection)) { + return computedDirection as 'ltr' | 'rtl'; + } + + return defaultDirection; +};