Skip to content

Commit

Permalink
Add horizontal scrolling to IDE component on small viewports (#897)
Browse files Browse the repository at this point in the history
* added horizontal scroll to editor content

* added horizontal scroll to editor tabs

* scroll to active tab when active tab changes

* mock scrollIntoView

* update snapshots

* add changeset

* only scroll in inline direction

* remove debug line
  • Loading branch information
joshfarrant authored Jan 23, 2025
1 parent e6dd060 commit 1c47b76
Show file tree
Hide file tree
Showing 13 changed files with 51 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/thirty-swans-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react-brand': patch
---

Added horizontal scrolling to `IDE` component when viewed on small viewports
26 changes: 18 additions & 8 deletions packages/react/src/IDE/IDE.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.IDE__inner {
--brand-IDE-lineNumber-width: 45px;
background: var(--brand-IDE-default-bgColor);
border: var(--brand-borderWidth-thin) solid var(--brand-IDE-borderColor);
}
Expand Down Expand Up @@ -170,10 +171,11 @@
}

.IDE--default .IDE__Editor {
margin-left: var(--base-size-20);
padding-left: var(--base-size-20);
}

.IDE__Editor-tabs {
overflow-x: auto;
display: inline-flex;
background: var(--brand-IDE-default-editor-tabs-bgColor);
}
Expand All @@ -187,6 +189,7 @@
}

.IDE__Editor-tab {
flex-shrink: 0;
display: flex;
padding: var(--base-size-8) var(--base-size-12);
align-items: center;
Expand Down Expand Up @@ -262,6 +265,7 @@ img.IDE__Editor-tab-icon {
.IDE__Editor-content {
height: 100%;
padding-top: var(--base-size-16);
overflow-x: auto;
}

.IDE--default .IDE__Editor-content {
Expand All @@ -281,6 +285,14 @@ img.IDE__Editor-tab-icon {
margin: 0;
}

.IDE__Editor-content-wrapper {
display: flex;
}

.IDE__Editor-content-inner {
flex: 1;
}

.IDE__Editor--small .IDE__Editor-pane,
.IDE__Editor--small .IDE__Editor-lineNumbers {
font-size: calc(var(--base-size-12) - 1px) !important; /* workaround dotcom specificity */
Expand All @@ -300,18 +312,19 @@ img.IDE__Editor-tab-icon {
}

.IDE__Editor-pane--suggested {
margin-left: calc(-1 * var(--base-size-64));
padding-left: calc((var(--base-size-64) * 2) - var(--brand-IDE-lineNumber-width)) !important;
background-color: var(--brand-IDE-autoSuggestLine-bgColor);
}

.IDE__Editor-lineNumbers {
position: absolute;
z-index: 1;
font-family: var(--brand-fontStack-monospace);
font-weight: var(--brand-text-weight-200);
font-feature-settings: 'liga' 0, 'calt' 0;
font-variation-settings: normal;
letter-spacing: 0;
width: 45px;
flex: 0 0 var(--brand-IDE-lineNumber-width);
}

.IDE__Editor-lineNumber {
Expand Down Expand Up @@ -341,20 +354,17 @@ img.IDE__Editor-tab-icon {
.IDE__Editor pre {
color: var(--brand-color-text-default);
font-family: var(--brand-fontStack-monospace);
padding-left: var(--base-size-64);
padding-left: calc(var(--base-size-64) - var(--brand-IDE-lineNumber-width));
}

pre.IDE__Chat-copilot-indicator {
background-color: var(--brand-IDE-autoSuggest-bgColor);
padding-left: 0;
position: absolute;
display: inline-flex;
padding: var(--base-size-8) var(--base-size-24);
border-radius: var(--brand-borderRadius-small);
gap: var(--base-size-16);
margin: 0;
margin-left: 60px;
margin-top: var(--base-size-4);
margin: var(--base-size-4) 0 0 var(--base-size-16);

box-shadow: -9px 10px 39px 0px rgba(0, 0, 0, 0.25);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/IDE/IDE.module.css.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ declare const styles: {
readonly "IDE__Editor-tab-icon": string;
readonly "IDE__Editor-content": string;
readonly "IDE__Editor-pane": string;
readonly "IDE__Editor-content-wrapper": string;
readonly "IDE__Editor-content-inner": string;
readonly "IDE__Editor--small": string;
readonly "IDE__Editor-lineNumbers": string;
readonly "IDE__Editor--medium": string;
Expand Down
9 changes: 8 additions & 1 deletion packages/react/src/IDE/IDE.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@ describe('IDE', () => {
{name: 'File 3', alternativeText: 'Alt for File 3', code: 'Code for File 3'},
]

afterEach(cleanup)
beforeEach(() => {
Element.prototype.scrollIntoView = jest.fn()
})

afterEach(() => {
cleanup()
jest.clearAllMocks()
})

it('renders main elements correctly into the document', () => {
const {getByTestId} = render(
Expand Down
15 changes: 10 additions & 5 deletions packages/react/src/IDE/IDE.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import React, {

import {Avatar, Button, Text, TextInput} from '..'
import type {BaseProps} from '../component-helpers'
import {useTabs} from '../hooks/useTabs'
import {useTabs, type OnTabActivate} from '../hooks/useTabs'

/**
* Design tokens
Expand Down Expand Up @@ -404,9 +404,13 @@ const _Editor = memo(
setTimeouts(prev => [...prev, animationEndTimeout])
}, [hasAnimated, isAnimating])

const onTabActivate = useCallback(() => {
resetAnimation()
}, [resetAnimation])
const onTabActivate = useCallback<OnTabActivate>(
(_, activeTabRef) => {
activeTabRef?.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'})
resetAnimation()
},
[resetAnimation],
)

const tabs = useTabs({
defaultTab: activeTab.toString(),
Expand Down Expand Up @@ -454,7 +458,7 @@ const _Editor = memo(
{files.map((file, fileIndex) => (
<div {...tabs.getTabPanelProps(fileIndex.toString())} key={file.name}>
<span className="visually-hidden">{file.alternativeText}</span>
<div aria-hidden="true">
<div className={styles['IDE__Editor-content-wrapper']} aria-hidden="true">
{showLineNumbers && (
<div className={styles['IDE__Editor-lineNumbers']}>
{(Array.isArray(file.code) ? file.code : file.code.split('\n')).map((_, index) => (
Expand All @@ -467,6 +471,7 @@ const _Editor = memo(
<div
ref={tabs.activeTab === fileIndex.toString() ? presRef : null}
data-testid={testIds.editorContent}
className={styles['IDE__Editor-content-inner']}
>
{Array.isArray(file.code) ? (
(file.code as string[]).map((line, index) => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 8 additions & 8 deletions packages/react/src/hooks/useTabs.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import {useCallback, useState, useRef, type HTMLAttributes, type KeyboardEvent} from 'react'
import {useId} from '../hooks/useId'

type TabOrientation = 'horizontal' | 'vertical'
export type OnTabActivate = (id: string, activeTabRef?: HTMLElement) => void

type UseTabsOptions = {
export type UseTabsOptions = {
defaultTab?: string
autoActivate?: boolean
onTabActivate?: (id: string) => void
orientation?: TabOrientation
onTabActivate?: OnTabActivate
orientation?: 'horizontal' | 'vertical'
}

type TabState = {
export type TabState = {
activeTab: string | null
focusedTab: string | null
tabs: Set<string>
}

type TabListProps = {
export type TabListProps = {
label?: string
labelledBy?: string
}

type UseTabs = {
export type UseTabs = {
activeTab: string | null
focusedTab: string | null
activateTab: (id: string) => void
Expand Down Expand Up @@ -50,7 +50,7 @@ export const useTabs = ({
const nextState = updater(prev)

if (nextState.activeTab && nextState.activeTab !== prev.activeTab) {
onTabActivate?.(nextState.activeTab)
onTabActivate?.(nextState.activeTab, tabRefs.current.get(nextState.activeTab))
}

return nextState
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1c47b76

Please sign in to comment.