Skip to content
Merged
13 changes: 13 additions & 0 deletions lib/components/DualScrollSync/DualScrollSync.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,17 @@ describe('DualScrollSync', () => {
expect(getByTestId('test')).toBeInTheDocument();
expect(getByText('Child Heading')).toBeInTheDocument();
});

it('should apply custom className and style', () => {
const { getByTestId } = render(
<DualScrollSyncBase id="test" className="custom-class" style={{ borderWidth: '1px' }}>
Styled Nav
</DualScrollSyncBase>
);

const label = getByTestId('test');

expect(label).toHaveClass('custom-class');
expect(label).toHaveStyle('border-width: 1px');
});
});
12 changes: 10 additions & 2 deletions lib/components/DualScrollSync/DualScrollSync.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import clsx from 'clsx';
import type { FC } from 'react';
import { useMemo } from 'react';

Expand All @@ -15,9 +16,11 @@ import { DualScrollSyncNavItem } from './DualScrollSyncNavItem';

export const DualScrollSyncBase: FC<DualScrollSyncProps> = ({
children,
className,
id,
items,
onItemClick
onItemClick,
style = {}
}) => {
const baseId = id ?? 'dual-scroll-sync';
const navId = `${baseId}-nav`;
Expand Down Expand Up @@ -57,7 +60,12 @@ export const DualScrollSyncBase: FC<DualScrollSyncProps> = ({

return (
<DualScrollSyncContext.Provider value={value}>
<section id={baseId} data-testid={baseId} className={styles.scrollSync}>
<section
className={clsx(styles.scrollSync, className)}
data-testid={baseId}
id={baseId}
style={style}
>
{items ? (
<>
<DualScrollSyncNav>
Expand Down
4 changes: 2 additions & 2 deletions lib/components/DualScrollSync/DualScrollSync.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type DualScrollSyncOptions = {

export type DualScrollSyncItem = PropsWithChildren<DualScrollSyncOptions>;

export type DualScrollSyncProps = PropsWithChildren<{
export type DualScrollSyncProps = PropsWithChildren<DualScrollSyncStyleProps> & {
/**
* Unique identifier for the DualScrollSync component. (Optional)
* @default 'dual-scroll-sync'
Expand All @@ -41,7 +41,7 @@ export type DualScrollSyncProps = PropsWithChildren<{
* @default () => {}
*/
onItemClick?: (activeKey: string) => void;
}>;
};

export type DualScrollSyncType = FC<DualScrollSyncProps> & {
Nav: FC<DualScrollSyncNavProps>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,17 @@ describe('DualScrollSyncContent', () => {

expect(getByTestId('test-content-id')).toBeInTheDocument();
});

it('should apply custom className and style', () => {
const { getByTestId } = render(
<DualScrollSyncContent className="custom-class" style={{ borderWidth: '1px' }}>
<div>Styled Content</div>
</DualScrollSyncContent>
);

const content = getByTestId('test-content-id');

expect(content).toHaveClass('custom-class');
expect(content).toHaveStyle('border-width: 1px');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export const DualScrollSyncContent: FC<DualScrollSyncContentProps> = ({
return (
<section
className={clsx(styles.scrollSyncContent, className)}
style={style}
data-testid={contentId}
id={contentId}
ref={contentRef}
style={style}
>
{children}
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,21 @@ describe('DualScrollSyncContentSection', () => {
expect(contentSection).toBeInTheDocument();
expect(getByText('Test Content')).toBeInTheDocument();
});

it('should apply custom className and style', () => {
const { getByTestId } = render(
<DualScrollSyncContentSection
sectionKey="styled-section"
className="custom-class"
style={{ borderWidth: '1px' }}
>
<div>Styled Content</div>
</DualScrollSyncContentSection>
);

const contentSection = getByTestId('test-content-id-section-styled-section');

expect(contentSection).toHaveClass('custom-class');
expect(contentSection).toHaveStyle({ borderWidth: '1px' });
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import clsx from 'clsx';
import type { FC } from 'react';

import { useDualScrollSyncContext } from '@/hooks';
Expand All @@ -6,23 +7,26 @@ import styles from './DualScrollSyncContentSection.module.scss';
import type { DualScrollSyncContentSectionProps } from './DualScrollSyncContentSection.types';

export const DualScrollSyncContentSection: FC<DualScrollSyncContentSectionProps> = ({
children,
className,
sectionKey,
children
style = {}
}) => {
const { contentId, sectionRefs } = useDualScrollSyncContext();

const contentSectionId = `${contentId}-section-${sectionKey}`;

return (
<article
className={styles.scrollSyncContentSection}
className={clsx(styles.scrollSyncContentSection, className)}
data-section={sectionKey}
data-testid={contentSectionId}
id={contentSectionId}
ref={(contentRef) => {
if (!contentRef) return;
sectionRefs.current[sectionKey] = contentRef;
}}
style={style}
>
{children}
</article>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,31 @@ describe('DualScrollSyncLabel', () => {
expect(getByText('Bold Label')).toBeInTheDocument();
expect(getByText('Bold Label').tagName).toBe('STRONG');
});

it('should apply custom className and style', () => {
const { getByText } = render(
<DualScrollSyncLabel className="custom-class" style={{ borderWidth: '1px' }}>
Styled Label
</DualScrollSyncLabel>
);

const label = getByText('Styled Label');

expect(label).toHaveClass('custom-class');
expect(label).toHaveStyle('border-width: 1px');
});

it('should not apply custom className or style if children is not a string', () => {
const { getByText } = render(
<DualScrollSyncLabel className="custom-class" style={{ borderWidth: '1px' }}>
<span>Styled Child</span>
</DualScrollSyncLabel>
);

const label = getByText('Styled Child');

expect(label).toBeInTheDocument();
expect(label).not.toHaveClass('custom-class');
expect(label).not.toHaveStyle('border-width: 1px');
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import clsx from 'clsx';
import type { FC } from 'react';

import { useDualScrollSyncContext } from '@/hooks';

import styles from './DualScrollSyncLabel.module.scss';
import type { DualScrollSyncLabelProps } from './DualScrollSyncLabel.types';

export const DualScrollSyncLabel: FC<DualScrollSyncLabelProps> = ({ children }) => {
export const DualScrollSyncLabel: FC<DualScrollSyncLabelProps> = ({
children,
className,
style = {}
}) => {
useDualScrollSyncContext();

if (typeof children !== 'string') return children;

return (
<span className={styles.scrollSyncContentSectionLabel} title={children}>
<span
className={clsx(styles.scrollSyncContentSectionLabel, className)}
title={children}
style={style}
>
{children}
</span>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { PropsWithChildren } from 'react';

export type DualScrollSyncLabelProps = PropsWithChildren;
import type { DualScrollSyncStyleProps } from '../DualScrollSync.types';

export type DualScrollSyncLabelProps = PropsWithChildren<DualScrollSyncStyleProps>;
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,17 @@ describe('DualScrollSyncNav', () => {
const navElement = getByTestId('test-nav-id');
expect(navElement).toHaveStyle({ '--menu-nav-visible-count': '3' });
});

it('should apply custom className and style', () => {
const { getByTestId } = render(
<DualScrollSyncNav className="custom-class" style={{ borderWidth: '1px' }}>
<div>Styled Content</div>
</DualScrollSyncNav>
);

const nav = getByTestId('test-nav-id');

expect(nav).toHaveClass('custom-class');
expect(nav).toHaveStyle('border-width: 1px');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import type { DualScrollSyncNavProps } from './DualScrollSyncNav.types';
export const DualScrollSyncNav: FC<DualScrollSyncNavProps> = ({
children,
className,
style = {},
maxVisibleItems = 6
maxVisibleItems = 6,
style = {}
}) => {
const { navId, navRef } = useDualScrollSyncContext();
const navItemCount = Children.count(children);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,21 @@ describe('DualScrollSyncNavItem', () => {
expect(navItem).toBeInTheDocument();
expect(getByText('Custom Child')).toBeInTheDocument();
});

it('should apply custom className and style', () => {
const { getByTestId } = render(
<DualScrollSyncNavItem
sectionKey="custom"
className="custom-class"
style={{ borderWidth: '1px' }}
>
<div>Styled Content</div>
</DualScrollSyncNavItem>
);

const navItem = getByTestId('test-nav-id-item-custom');

expect(navItem).toHaveClass('custom-class');
expect(navItem).toHaveStyle('border-width: 1px');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import styles from './DualScrollSyncNavItem.module.scss';
import type { DualScrollSyncNavItemProps } from './DualScrollSyncNavItem.types';

export const DualScrollSyncNavItem: FC<DualScrollSyncNavItemProps> = ({
sectionKey,
children,
className,
style
sectionKey,
style = {}
}) => {
const { navId, onMenuItemSelect, onItemClick, activeKey, navItemRefs } =
useDualScrollSyncContext();
Expand Down