Skip to content

Commit

Permalink
Refactor TogglePrimitive component for controlled state
Browse files Browse the repository at this point in the history
  • Loading branch information
kotAPI committed Nov 24, 2024
1 parent b8a632f commit 2694fba
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 7 deletions.
32 changes: 25 additions & 7 deletions src/core/primitives/Toggle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,45 @@ import React, { useState } from 'react';
import Primitive from '~/core/primitives/Primitive';

export interface TogglePrimitiveProps {
defaultPressed? : boolean | false;
pressed: boolean;
defaultPressed?: boolean;
pressed?: boolean;
children?: React.ReactNode;
className?: string;
label?: string;
disabled?: boolean;
onPressedChange : (isPressed:boolean) => void;

onPressedChange: (isPressed: boolean) => void;
}
const TogglePrimitive = ({ children, label = '', defaultPressed, pressed, onPressedChange = () => {}, disabled, ...props }:TogglePrimitiveProps) => {
const [isPressed, setIsPressed] = useState(pressed || defaultPressed);

const TogglePrimitive = ({
children,
label = '',
defaultPressed = false,
pressed: controlledPressed,
onPressedChange = () => {},
disabled,
...props
}: TogglePrimitiveProps) => {
const [uncontrolledPressed, setUncontrolledPressed] = useState(defaultPressed);

const isControlled = controlledPressed !== undefined;
const isPressed = isControlled ? controlledPressed : uncontrolledPressed;

const handlePressed = () => {
if (disabled) {
return;

Check warning on line 31 in src/core/primitives/Toggle/index.tsx

View check run for this annotation

Codecov / codecov/patch

src/core/primitives/Toggle/index.tsx#L31

Added line #L31 was not covered by tests
}

const updatedPressed = !isPressed;
setIsPressed(updatedPressed);
if (!isControlled) {
setUncontrolledPressed(updatedPressed);
}
onPressedChange(updatedPressed);
};

const ariaAttributes:any = label ? { 'aria-label': label } : {};
ariaAttributes['aria-pressed'] = isPressed ? 'true' : 'false';
ariaAttributes['aria-disabled'] = disabled ? 'true' : 'false';

return <Primitive.button
onClick={handlePressed}
data-state={isPressed ? 'on' : 'off'}
Expand Down
63 changes: 63 additions & 0 deletions src/core/primitives/Toggle/tests/TogglePrimitive.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,67 @@ describe('TogglePrimitive', () => {
render(<TogglePrimitive disabled>Test Content</TogglePrimitive>);
expect(screen.getByRole('button')).toHaveAttribute('aria-disabled', 'true');
});

it('renders in controlled mode correctly', () => {
const onPressedChange = jest.fn();
const { rerender } = render(
<TogglePrimitive pressed={false} onPressedChange={onPressedChange}>
Test Content
</TogglePrimitive>
);
expect(screen.getByRole('button')).toHaveAttribute('data-state', 'off');

// Click should trigger onPressedChange but not change state directly
fireEvent.click(screen.getByRole('button'));
expect(onPressedChange).toHaveBeenCalledWith(true);
expect(screen.getByRole('button')).toHaveAttribute('data-state', 'off');

// State should only change when pressed prop changes
rerender(
<TogglePrimitive pressed={true} onPressedChange={onPressedChange}>
Test Content
</TogglePrimitive>
);
expect(screen.getByRole('button')).toHaveAttribute('data-state', 'on');
});

it('handles multiple clicks correctly in uncontrolled mode', () => {
render(<TogglePrimitive>Test Content</TogglePrimitive>);
const button = screen.getByRole('button');

fireEvent.click(button);
expect(button).toHaveAttribute('data-state', 'on');

fireEvent.click(button);
expect(button).toHaveAttribute('data-state', 'off');
});

it('prevents state change when disabled', () => {
const onPressedChange = jest.fn();
render(
<TogglePrimitive disabled onPressedChange={onPressedChange}>
Test Content
</TogglePrimitive>
);

fireEvent.click(screen.getByRole('button'));
expect(onPressedChange).not.toHaveBeenCalled();
expect(screen.getByRole('button')).toHaveAttribute('data-state', 'off');
});

it('maintains controlled state after multiple clicks', () => {
const onPressedChange = jest.fn();
render(
<TogglePrimitive pressed={false} onPressedChange={onPressedChange}>
Test Content
</TogglePrimitive>
);
const button = screen.getByRole('button');

fireEvent.click(button);
fireEvent.click(button);

expect(onPressedChange).toHaveBeenCalledTimes(2);
expect(button).toHaveAttribute('data-state', 'off');
});
});

0 comments on commit 2694fba

Please sign in to comment.