Skip to content

Conversation

@akmal-deriv
Copy link
Collaborator

Description

  • feat: add RTL support to VideoPlayer component and its controls

Related Issue: Fixes #(issue)

Type of Change

  • πŸ› Bug fix
  • ✨ New feature
  • πŸ’₯ Breaking change
  • πŸ”§ Refactoring
  • πŸ“š Documentation
  • πŸ§ͺ Tests

Testing

  • Unit tests pass
  • Manual testing completed
  • No console errors
  • No test needed

Screenshots

(Add screenshots if UI changes)

Before:

After:

@github-actions
Copy link
Contributor

github-actions bot commented Oct 30, 2025

Dependency Review

βœ… No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails

Scanned Manifest Files

@claude
Copy link

claude bot commented Oct 30, 2025

Claude finished @akmal-deriv's task β€”β€” View job


Code Review Complete

I've reviewed the RTL support implementation for the VideoPlayer component. Here's my comprehensive analysis:


Summary

The PR successfully adds RTL (right-to-left) support to the VideoPlayer component using a smart approach: forcing LTR direction on the video player itself while applying transform mirroring to the progress bar. This is a common pattern for video players in RTL interfaces.

Priority Breakdown:

  • 🟒 Low priority: 3 items (code quality improvements)
  • 🟑 Medium priority: 1 item (error handling)
  • πŸ”΄ High priority: 0 items

Detailed Review

βœ… What Works Well

  1. Correct RTL Strategy: The approach of keeping the video controls in LTR but transforming the progress bar is the right pattern for video players in RTL contexts.

  2. Safe Hook Usage: The useSafeIsRtl() wrapper (video-player.tsx:13-19) properly catches errors from useIsRtl(), preventing crashes if the hook is used outside its required context.

  3. Test Coverage: Good addition of RTL-specific test coverage (video-player.spec.tsx:140-156) to verify the behavior.

  4. SCSS Direction Enforcement: Proper use of direction: ltr !important in key places to maintain video control directionality.


Issues & Recommendations

🟑 Medium Priority

1. Incomplete Transform Application (video-player.scss:6)

Location: packages/components/src/components/video-player/video-player.scss:6

Issue: The wrapper has direction: ltr !important but the SCSS comment mentions transform handling should be dynamic based on RTL state:

&__wrapper {
    position: relative;
    direction: ltr !important;
}

The comment on line 81 states:

// Transform is now handled dynamically in the component based on RTL state

However, I don't see any dynamic transform being applied to the wrapper itself, only to the progress bar.

Recommendation:

  • If the wrapper doesn't need dynamic transform, remove the misleading comment on line 81
  • If it does need it, apply the transform similar to the progress bar approach

🟒 Low Priority - Code Quality

2. Prop Default Value Redundancy (video-controls.tsx:53)

Location: packages/components/src/components/video-player/video-controls.tsx:53

Issue: The is_rtl prop has a default value in the destructuring:

is_rtl = false,

But it's also marked as optional in the type definition (line 24). This is technically fine but creates redundancy.

Recommendation: Consider whether this default should remain or if it should always be explicitly passed from the parent.


3. Test Coverage for scaleX Transform

Location: packages/components/src/components/video-player/__tests__/video-player.spec.tsx:140-156

Issue: The test "should render progress bar correctly in RTL mode without reversing direction" checks that elements exist but doesn't verify the actual transform: scaleX(-1) style is applied.

Current test:

it('should render progress bar correctly in RTL mode without reversing direction', () => {
    (useIsRtl as jest.Mock).mockReturnValue(true);
    render(<VideoPlayer {...mocked_props} />);
    // ... checks for element existence only
});

Recommendation: Add assertion to verify the transform style:

const progress_bar = screen.getByTestId('dt_progress_bar');
expect(progress_bar).toHaveStyle({ transform: 'scaleX(-1)' });

4. Error Handling Feedback

Location: packages/components/src/components/video-player/video-player.tsx:13-19

Issue: The useSafeIsRtl() catch block silently swallows errors:

try {
    return useIsRtl();
} catch (_error) {
    return false;
}

Recommendation: Consider logging the error in development mode to help with debugging:

try {
    return useIsRtl();
} catch (error) {
    if (process.env.NODE_ENV === 'development') {
        console.warn('useIsRtl hook failed:', error);
    }
    return false;
}

Security, Performance & Best Practices

βœ… Security: No security concerns identified. No XSS risks, no sensitive data exposure.

βœ… Performance: No performance issues. The useSafeIsRtl() hook is called once per render which is appropriate. The transform is a CSS property with no performance impact.

βœ… React Best Practices: Code follows React/TypeScript conventions properly.

βœ… Type Safety: All new props are properly typed.


Testing Recommendations

The existing test coverage is good but could be enhanced:

  1. Transform Style Verification: As mentioned above, verify the actual CSS transform is applied
  2. Edge Case: Test behavior when useIsRtl() throws an error to ensure graceful fallback
  3. Visual Regression: If you have visual regression testing, capture RTL vs LTR screenshots

Verdict

βœ… Approved for merge with minor improvements recommended.

The implementation is solid and follows the correct pattern for RTL video player support. The identified issues are all low-to-medium priority and don't block the PR from being merged. They can be addressed in follow-up improvements if desired.


@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Oct 30, 2025

Deploying derivatives-trader with Β Cloudflare Pages Β Cloudflare Pages

Latest commit: a2ddc05
Status:Β βœ…Β  Deploy successful!
Preview URL: https://e32ce420.derivatives-trader.pages.dev
Branch Preview URL: https://b-video-player-ltr.derivatives-trader.pages.dev

View logs

nijil-deriv
nijil-deriv previously approved these changes Oct 31, 2025
@akmal-deriv
Copy link
Collaborator Author

akmal-deriv commented Nov 3, 2025

πŸ€– AI Code Analysis Results

πŸ“ Manual Coding

⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%

0 of 1,446 characters (0%) in newly added lines are AI-generated


πŸ“Š Analysis Summary

Metric Value
πŸ“ Files Changed 4
βž• New Lines Added 1,446 chars
πŸ€– AI Characters 0
πŸ“ˆ AI Percentage 0%

πŸ“‹ Per-File Breakdown (New Lines Only)

File Lines Added AI Content Percentage
...layer/__tests__/video-player.spec.tsx 858 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...nents/video-player/video-controls.tsx 218 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...onents/video-player/video-player.scss 179 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...ponents/video-player/video-player.tsx 191 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%

πŸ“ This PR contains traditional hand-written code.

πŸ“Š Raw Data (for dashboard)
{
  "timestamp": "2025-11-05T06:30:43.331Z",
  "repository": "deriv-com/derivatives-trader",
  "branch": "unknown",
  "author": "unknown",
  "summary": {
    "totalCharacters": 1446,
    "aiCharacters": 0,
    "percentage": 0
  },
  "files": [
    {
      "name": "packages/components/src/components/video-player/__tests__/video-player.spec.tsx",
      "totalCharacters": 858,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "\nimport { useIsRtl } from '@deriv/api';\nimport VideoPlayer from '../video-player';\n\njest.mock('@deriv/api', () => ({\n    useIsRtl: jest.fn(() => false),\n}));\n    it('should render progress bar correctly in RTL mode without reversing direction', () => {\n        (useIsRtl as jest.Mock).mockReturnValue(true);\n\n        render(<VideoPlayer {...mocked_props} />);\n        const video = screen.getByTestId(video_data_testid);\n        fireEvent.loadedData(video);\n\n        expect(screen.getByTestId(player_..."
    },
    {
      "name": "packages/components/src/components/video-player/video-controls.tsx",
      "totalCharacters": 218,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "import clsx from 'clsx';\n\n\nimport Text from '../text';\n\nimport VolumeControl from './volume-control';\n    is_rtl?: boolean;\n    is_rtl = false,\n                style={{ transform: is_rtl ? 'scaleX(-1)' : 'scaleX(1)' }}"
    },
    {
      "name": "packages/components/src/components/video-player/video-player.scss",
      "totalCharacters": 179,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "        direction: ltr;\n            direction: ltr;\n                direction: ltr;\n            direction: ltr;\n                direction: ltr;\n                    direction: ltr;"
    },
    {
      "name": "packages/components/src/components/video-player/video-player.tsx",
      "totalCharacters": 191,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "import throttle from 'lodash.throttle';\nimport { useDebounceCallback } from 'usehooks-ts';\n\n\nimport VideoOverlay from './video-overlay';\n    } catch (_error) {\n                is_rtl={is_rtl}"
    }
  ]
}

πŸš€ Analysis powered by ShiftAI β€’ Analyzes only PR changes, not entire files

@claude
Copy link

claude bot commented Nov 3, 2025

Claude finished @akmal-deriv's task β€”β€” View job


Code Review Complete βœ…

I've reviewed the RTL support implementation for the VideoPlayer component. Here's my comprehensive analysis:


Summary

This PR successfully adds RTL (right-to-left) support to the VideoPlayer component. The implementation uses a smart approach: forcing LTR direction on the video player controls while applying CSS transform mirroring (scaleX(-1)) to the progress bar. This is the correct pattern for video players in RTL interfaces.

Priority Breakdown:

  • πŸ”΄ High priority: 0 items
  • 🟑 Medium priority: 2 items (logic bug + test coverage gap)
  • 🟒 Low priority: 3 items (code quality improvements)

Detailed Review

βœ… What Works Well

  1. Correct RTL Strategy: The approach of keeping video controls in LTR but transforming the progress bar is the industry-standard pattern for video players in RTL contexts.

  2. Safe Hook Usage: The useSafeIsRtl() wrapper (video-player.tsx:13-19) properly catches errors from useIsRtl(), preventing crashes if the hook is used outside its required context.

  3. Clean Implementation: The changes are minimal and focused, which reduces the risk of introducing bugs.

  4. SCSS Direction Enforcement: Proper use of direction: ltr !important to maintain video control directionality.


Issues & Recommendations

🟑 Medium Priority

1. Critical Logic Fix: Removed Width Calculation Logic

Location: video-player.tsx:108-113 (commit f750272)

Issue: The commit removed this important line from calculateNewWidth():

if (is_rtl) new_width = full_width - new_width;

However, this removal appears to be intentional to match the new CSS-based transform approach. But I'm concerned this might cause incorrect behavior:

  • The progress bar now uses CSS transform: scaleX(-1) to mirror visually
  • But the click/drag calculations might not account for this transform
  • When a user clicks on the progress bar in RTL mode, the click position calculation might be incorrect

Recommendation:

  • Test thoroughly: Click different positions on the progress bar in RTL mode to verify the video seeks to the correct position
  • If clicking the right side of the bar seeks to the beginning (and vice versa), the removed line needs to be restored, OR the calculateNewWidth function needs to account for the transform differently
  • The visual transform doesn't automatically adjust mouse event coordinates

2. Incomplete Test Coverage

Location: video-player.spec.tsx:140-156

Issue: The new RTL test only checks that elements exist, but doesn't verify:

  1. The actual transform: scaleX(-1) style is applied
  2. The progress bar interaction works correctly in RTL mode (clicking/dragging)

Current test:

it('should render progress bar correctly in RTL mode without reversing direction', () => {
    (useIsRtl as jest.Mock).mockReturnValue(true);
    render(<VideoPlayer {...mocked_props} />);
    // ... only checks element existence
});

Recommendation: Enhance the test:

it('should render progress bar correctly in RTL mode without reversing direction', () => {
    (useIsRtl as jest.Mock).mockReturnValue(true);
    render(<VideoPlayer {...mocked_props} />);
    const video = screen.getByTestId(video_data_testid);
    fireEvent.loadedData(video);

    const progress_bar = screen.getByTestId('dt_progress_bar');
    expect(progress_bar).toHaveStyle({ transform: 'scaleX(-1)' });
    
    // Test interaction: click progress bar in RTL mode
    await userEvent.click(progress_bar);
    // Add assertions to verify correct seek position
});

🟒 Low Priority - Code Quality

3. Misleading SCSS Comment

Location: video-player.scss:80-81

Issue: The comment states:

// Transform is now handled dynamically in the component based on RTL state

But this appears above the __wrapper element (line 68-89), which doesn't have any dynamic transform. The transform is only applied to the progress bar, not the wrapper.

Recommendation:

  • Move this comment to line 91 (above __progress-bar) where the transform actually occurs, OR
  • Remove it if it's no longer relevant

4. Excessive !important Usage in SCSS

Location: video-player.scss:6, 100, 117, 135, 142, 149

Issue: The PR adds direction: ltr !important to 7 different elements:

  • __wrapper (line 6)
  • __progress-bar (line 100)
  • __progress-bar__filled (line 117)
  • __bottom-bar (line 135)
  • __bottom-bar--v2 (line 142)
  • .controls__right--v2 (line 149)

This seems excessive. If direction: ltr !important is set on the __wrapper (line 6), child elements should inherit it unless something is explicitly overriding it.

Recommendation:

  • Test if only setting direction: ltr !important on __wrapper is sufficient
  • Remove redundant declarations on child elements if inheritance works
  • This would make the code cleaner and easier to maintain

5. Prop Default Value Redundancy

Location: video-controls.tsx:53

Issue: The is_rtl prop has both:

  • Optional type definition: is_rtl?: boolean; (line 24)
  • Default value in destructuring: is_rtl = false, (line 53)

Recommendation: This is fine but consider the pattern: if all optional props use default values in destructuring, this is consistent. If not, consider standardizing the approach.


6. Import Organization Changes

Location: Multiple files

Issue: The PR includes import reordering changes that aren't related to the RTL feature:

  • video-player.tsx: Moving throttle and useDebounceCallback imports
  • video-controls.tsx: Reordering multiple imports

Recommendation: While import organization is good, consider keeping refactoring separate from feature PRs to make reviews easier. For this PR, it's acceptable but something to keep in mind.


Security, Performance & Best Practices

βœ… Security: No security concerns identified. No XSS risks, no sensitive data exposure.

βœ… Performance: No performance issues. The useSafeIsRtl() hook is called once per render. The CSS transform has no measurable performance impact.

βœ… React Best Practices: Code follows React/TypeScript conventions properly. Proper use of memoization with React.memo on the component.

βœ… Type Safety: All new props are properly typed with TypeScript.

⚠️ Potential Regression Risk: The removal of the is_rtl width calculation logic (see Medium Priority Issue #1) needs manual testing to ensure progress bar interactions work correctly.


Testing Recommendations

Required:

  1. Manual RTL Testing (Critical):

    • Switch to an RTL language
    • Click various positions on the progress bar - verify the video seeks to the expected position
    • Drag the progress dot - verify it moves correctly and the video time updates properly
    • This will verify if Issue build(deps-dev): bump webpack-dev-server from 5.2.0 to 5.2.1Β #1 is a real problem or if the CSS transform approach works correctly
  2. Visual Testing:

    • Verify the progress bar visually mirrors correctly in RTL mode
    • Ensure other controls remain in their correct positions

Nice to Have:
3. Automated Test Improvements:

  • Add style assertion for transform: scaleX(-1)
  • Add interaction tests for clicking/dragging in RTL mode
  • Test the useSafeIsRtl() error handling path

Verdict

⚠️ Conditional Approval - Approved pending verification of Issue #1.

The implementation follows the correct pattern for RTL video player support. However, there's a critical concern about whether progress bar interactions work correctly in RTL mode after removing the width calculation adjustment.

Before merging:

  1. βœ… Must do: Manually test progress bar clicking/dragging in RTL mode
  2. βœ… Must do: If Issue build(deps-dev): bump webpack-dev-server from 5.2.0 to 5.2.1Β #1 causes incorrect behavior, either restore the calculation logic OR adjust the click position calculation to account for the CSS transform
  3. πŸ”΅ Should do: Enhance test coverage (Issue build(deps-dev): bump axios from 1.7.9 to 1.11.0Β #2)
  4. πŸ”΅ Nice to have: Address code quality issues (build(deps): bump @babel/helpers from 7.26.7 to 7.27.6Β #3-6)

Issues #3-6 are minor and don't block merging, but Issue #1 and #2 should be addressed to ensure the feature works correctly.


nijil-deriv
nijil-deriv previously approved these changes Nov 3, 2025
@claude
Copy link

claude bot commented Nov 5, 2025

Claude finished @akmal-deriv's task β€”β€” View job


Code Review Complete βœ…

I've thoroughly reviewed the RTL support implementation for the VideoPlayer component. Here's my comprehensive analysis:


Summary

This PR adds RTL (right-to-left) support to the VideoPlayer component using a CSS-based approach: forcing LTR direction on video controls while applying scaleX(-1) transform to the progress bar. This is the standard pattern for video players in RTL interfaces.

Priority Breakdown:

  • πŸ”΄ High priority: 1 item (critical logic change)
  • 🟑 Medium priority: 1 item (test coverage)
  • 🟒 Low priority: 2 items (code quality)

Detailed Review

βœ… What Works Well

  1. Correct RTL Strategy: Keeping video controls in LTR while mirroring only the progress bar is the industry-standard approach for video players in RTL contexts.

  2. Safe Hook Usage: The useSafeIsRtl() wrapper (video-player.tsx:13-19) properly catches errors from useIsRtl(), preventing crashes if used outside context.

  3. Clean Implementation: Minimal, focused changes reduce risk of introducing bugs.

  4. Test Addition: Good addition of RTL-specific test case.


Issues & Recommendations

πŸ”΄ High Priority - Critical

1. Removed Width Calculation for RTL (video-player.tsx:111)

Issue: This line was removed in the changes:

if (is_rtl) new_width = full_width - new_width;

Analysis:

  • Previously, click position calculations were adjusted for RTL mathematically
  • Now, the visual mirroring is handled by CSS transform: scaleX(-1)
  • However, CSS transforms don't automatically adjust mouse event coordinates
  • When a user clicks the right side of the progress bar in RTL mode, the browser still reports coordinates as if it were LTR
  • The calculateNewWidth function uses clientX coordinates which are not affected by CSS transforms

Expected Behavior:

  • RTL mode with transform: Click right side β†’ should seek to beginning
  • Current code: Click right side β†’ likely seeks to end (incorrect)

Recommendation:
⚠️ This needs immediate testing and likely a fix. Either:

  1. Restore the calculation: Add back if (is_rtl) new_width = full_width - new_width; after line 110, OR
  2. Remove the transform approach: Keep the mathematical RTL adjustment and remove the CSS transform

The CSS transform creates a visual mirror but doesn't fix the interaction logic. You need both the visual transform AND the coordinate adjustment.

Suggested fix:

let new_width =
    ((client_X - shift_X - (progress_bar?.getBoundingClientRect().left ?? 0)) /
        (progress_bar?.getBoundingClientRect().width ?? 0)) *
    full_width;
if (is_rtl) new_width = full_width - new_width; // Restore this line
if (new_width >= full_width) new_width = full_width;
if (new_width <= 0) new_width = 0;

🟑 Medium Priority

2. Insufficient Test Coverage (video-player.spec.tsx:140-156)

Issue: The RTL test only verifies elements exist, not the actual behavior:

it('should render progress bar correctly in RTL mode without reversing direction', () => {
    (useIsRtl as jest.Mock).mockReturnValue(true);
    render(<VideoPlayer {...mocked_props} />);
    // ... only checks element existence
});

Missing test coverage:

  1. Verify transform: scaleX(-1) style is applied
  2. Test progress bar click interaction in RTL mode
  3. Test dragging behavior in RTL mode

Recommendation:

it('should render progress bar correctly in RTL mode without reversing direction', () => {
    (useIsRtl as jest.Mock).mockReturnValue(true);
    render(<VideoPlayer {...mocked_props} />);
    const video = screen.getByTestId(video_data_testid);
    fireEvent.loadedData(video);

    const progress_bar = screen.getByTestId('dt_progress_bar');
    
    // Verify the transform is applied
    expect(progress_bar).toHaveStyle({ transform: 'scaleX(-1)' });
    
    // Test interaction (if Issue #1 is fixed)
    await userEvent.click(progress_bar);
    // Add assertions for seek position
});

🟒 Low Priority - Code Quality

3. Excessive direction: ltr Declarations (video-player.scss)

Issue: Added direction: ltr to 6 different elements:

  • __wrapper (line 6)
  • __progress-bar (line 99)
  • __progress-bar__filled (line 116)
  • __bottom-bar (line 134)
  • __bottom-bar--v2 (line 141)
  • .controls__right--v2 (line 148)

Analysis: If direction: ltr is set on __wrapper (line 6), child elements should inherit it unless overridden.

Recommendation:

  • Test if only setting on __wrapper is sufficient
  • Remove redundant declarations on child elements
  • Note: The reviewer comment asked about !important - none of these use !important, so the response about "gets overwritten" may not be accurate

4. Import Reordering (Multiple Files)

Issue: Includes unrelated import reorganization:

  • video-player.tsx: Moving throttle and useDebounceCallback
  • video-controls.tsx: Reordering multiple imports

Recommendation: While import organization is good, keep refactoring separate from feature PRs for easier reviews. Acceptable here, but keep in mind for future PRs.


Security, Performance & Best Practices

βœ… Security: No security concerns. No XSS risks, no sensitive data exposure.

βœ… Performance: No issues. useSafeIsRtl() called once per render. CSS transform has negligible performance impact.

βœ… React/TypeScript Best Practices: Code follows conventions properly. Good use of React.memo, proper typing.

βœ… Type Safety: All new props properly typed.

⚠️ Regression Risk: Issue #1 (removed width calculation) needs verification.


Testing Recommendations

Required Before Merge:

  1. Manual RTL Testing (Critical):

    • Switch to RTL language
    • Click various positions on progress bar
    • Verify video seeks to expected position (right side should go to beginning)
    • Drag the progress dot - verify correct behavior
  2. Automated Test Enhancement:

    • Add style assertion for transform
    • Add interaction tests for RTL mode

Verdict

⚠️ Conditional Approval - Requires verification of Issue #1

The implementation follows the correct pattern, but there's a critical concern about progress bar interaction in RTL mode.

Before merging:

  1. βœ… MUST: Test progress bar clicking/dragging in RTL mode manually
  2. βœ… MUST: If clicks are reversed, restore the width calculation logic (line 111: if (is_rtl) new_width = full_width - new_width;)
  3. πŸ”΅ Should: Enhance test coverage for RTL interactions
  4. πŸ”΅ Nice to have: Clean up redundant SCSS declarations

The CSS transform mirrors the visual appearance but doesn't automatically adjust mouse coordinates. You likely need both the visual transform AND the coordinate adjustment for correct behavior.


@nijil-deriv nijil-deriv merged commit bc360af into master Nov 6, 2025
11 checks passed
@nijil-deriv nijil-deriv deleted the b-video-player-ltr branch November 6, 2025 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants