Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
91f8be2
Implementing Checkbox.tsx
idanidan29 May 31, 2025
61e3de3
simplifying and containing Checkbox.tsx
idanidan29 May 31, 2025
a09dbbf
implementing SearchBar.tsx
idanidan29 May 31, 2025
b701fa0
adjusting border color
idanidan29 Jun 1, 2025
8c9d91c
adding cypress test file for checkbox
idanidan29 Jun 2, 2025
54291d4
added a test file for search bar
idanidan29 Jun 2, 2025
f89bd44
fixing issue #1279
idanidan29 Jun 2, 2025
a8d9ef9
fixing spacing
idanidan29 Jun 2, 2025
b8167ed
spacing adjustments
idanidan29 Jun 2, 2025
3c7b75d
mobile adjustment
idanidan29 Jun 2, 2025
30046ad
last spacing adjustments
idanidan29 Jun 3, 2025
94914e8
format adjustments
idanidan29 Jun 3, 2025
5365221
adding prop types for format fixing
idanidan29 Jun 3, 2025
f673ad7
adjusting the cypress test for searchbar
idanidan29 Jun 3, 2025
63b42fa
small format fix
idanidan29 Jun 3, 2025
c82dd4d
keeping original width
idanidan29 Jun 3, 2025
a677923
adding active filters counters
idanidan29 Jun 3, 2025
dd47d1d
implementing ShadCN button
idanidan29 Jun 3, 2025
f329833
implememnt radio.tsx using radioGroup from shadcn
idanidan29 Jun 4, 2025
a402879
adding hover effects to the sidbar sections
idanidan29 Jun 14, 2025
da0e427
adjusting hover effect color
idanidan29 Jun 14, 2025
2486336
adapt DropdownMenu to shadcn
idanidan29 Jun 14, 2025
691661b
creating test files for the adapted components
idanidan29 Jun 14, 2025
83a3f63
Merge branch 'main' into phase-1.2
idanidan29 Jun 14, 2025
1a73d27
adjusting dependencies
idanidan29 Jun 14, 2025
11052c0
Merge branch 'phase-1.2' of https://github.com/idanidan29/website_jso…
idanidan29 Jun 14, 2025
fee7adc
format adjustments
idanidan29 Jun 14, 2025
7119e5a
format adjustments
idanidan29 Jun 14, 2025
8bd59b5
final adjustments
idanidan29 Jun 14, 2025
9a7b69b
improving test coverege
idanidan29 Jun 14, 2025
fa95340
fixing filtring logic and checkbox visuals
idanidan29 Jun 20, 2025
9f3ebc3
format adjustments
idanidan29 Jun 20, 2025
538d526
adding tests for icon placment and size
idanidan29 Jun 20, 2025
66af35d
fixing linting issues
idanidan29 Jun 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions components/ui/collapsible.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable linebreak-style */
/* eslint-disable react/react-in-jsx-scope */
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';

function Collapsible({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
return <CollapsiblePrimitive.Root data-slot='collapsible' {...props} />;
}

function CollapsibleTrigger({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
return (
<CollapsiblePrimitive.CollapsibleTrigger
data-slot='collapsible-trigger'
{...props}
/>
);
}

function CollapsibleContent({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return (
<CollapsiblePrimitive.CollapsibleContent
data-slot='collapsible-content'
{...props}
/>
);
}

export { Collapsible, CollapsibleTrigger, CollapsibleContent };
45 changes: 45 additions & 0 deletions components/ui/radio-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint-disable linebreak-style */
/* eslint-disable react/prop-types */
import * as React from 'react';
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
import { CircleIcon } from 'lucide-react';

import { cn } from '@/lib/utils';

function RadioGroup({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
return (
<RadioGroupPrimitive.Root
data-slot='radio-group'
className={cn('grid gap-3', className)}
{...props}
/>
);
}

function RadioGroupItem({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
return (
<RadioGroupPrimitive.Item
data-slot='radio-group-item'
className={cn(
'border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...props}
>
<RadioGroupPrimitive.Indicator
data-slot='radio-group-indicator'
className='relative flex items-center justify-center'
>
<CircleIcon className='fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2' />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
);
}

export { RadioGroup, RadioGroupItem };
99 changes: 99 additions & 0 deletions cypress/components/Button.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* eslint-disable linebreak-style */
import React from 'react';
import { Button } from '@/components/ui/button';

describe('Button Component', () => {
it('renders with default variant and size', () => {
cy.mount(<Button>Click me</Button>);

cy.get('button')
.should('have.class', 'bg-primary')
.and('have.class', 'h-9')
.and('have.class', 'px-4')
.and('have.class', 'py-2');
});

it('renders with different variants', () => {
cy.mount(
<div className='flex gap-2'>
<Button variant='default'>Default</Button>
<Button variant='destructive'>Destructive</Button>
<Button variant='outline'>Outline</Button>
<Button variant='secondary'>Secondary</Button>
<Button variant='ghost'>Ghost</Button>
<Button variant='link'>Link</Button>
</div>,
);

cy.get('button').eq(0).should('have.class', 'bg-primary');
cy.get('button').eq(1).should('have.class', 'bg-destructive');
cy.get('button').eq(2).should('have.class', 'border');
cy.get('button').eq(3).should('have.class', 'bg-secondary');
cy.get('button').eq(4).should('have.class', 'hover:bg-accent');
cy.get('button').eq(5).should('have.class', 'text-primary');
});

it('renders with different sizes', () => {
cy.mount(
<div className='flex gap-2'>
<Button size='default'>Default</Button>
<Button size='sm'>Small</Button>
<Button size='lg'>Large</Button>
<Button size='icon'>Icon</Button>
</div>,
);

cy.get('button').eq(0).should('have.class', 'h-9');
cy.get('button').eq(1).should('have.class', 'h-8');
cy.get('button').eq(2).should('have.class', 'h-10');
cy.get('button').eq(3).should('have.class', 'size-9');
});

it('handles disabled state', () => {
cy.mount(<Button disabled>Disabled Button</Button>);

cy.get('button')
.should('be.disabled')
.and('have.class', 'disabled:opacity-50')
.and('have.class', 'disabled:pointer-events-none');
});

it('renders with icon', () => {
cy.mount(
<Button>
<svg data-testid='test-icon' />
Button with Icon
</Button>,
);

cy.get('button').should('have.class', 'has-[>svg]:px-3');
cy.get('[data-testid="test-icon"]').should('exist');
});

it('applies custom className', () => {
cy.mount(<Button className='custom-class'>Custom Button</Button>);

cy.get('button').should('have.class', 'custom-class');
});

it('handles click events', () => {
const onClickSpy = cy.spy().as('onClickSpy');

cy.mount(<Button onClick={onClickSpy}>Click me</Button>);

cy.get('button').click();
cy.get('@onClickSpy').should('have.been.calledOnce');
});

it('renders as child component when asChild is true', () => {
cy.mount(
<Button asChild>
<a href='#test'>Link Button</a>
</Button>,
);

cy.get('a')
.should('have.attr', 'href', '#test')
.and('have.class', 'bg-primary');
});
});
101 changes: 101 additions & 0 deletions cypress/components/DropdownMenu.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React from 'react';
import DropdownMenu from '@/pages/tools/components/ui/DropdownMenu';
import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils';

describe('DropdownMenu Component', () => {
let mockRouter: MockRouter;
const mockIcon = <svg data-testid='test-icon' />;
const mockChildren = <div data-testid='test-content'>Test Content</div>;

beforeEach(() => {
mockRouter = mockNextRouter();
});

it('renders with basic props', () => {
cy.mount(
<DropdownMenu label='Test Menu' icon={mockIcon} testMode={true}>
{mockChildren}
</DropdownMenu>,
);

cy.contains('Test Menu').should('be.visible');
cy.get('[data-testid="test-icon"]').should('exist');
});

it('shows content when clicked', () => {
cy.mount(
<DropdownMenu label='Test Menu' icon={mockIcon} testMode={true}>
{mockChildren}
</DropdownMenu>,
);

cy.get('button').click();
cy.get('[data-testid="test-content"]').should('be.visible');
});

it('displays count badge when count is provided', () => {
cy.mount(
<DropdownMenu label='Test Menu' icon={mockIcon} count={5} testMode={true}>
{mockChildren}
</DropdownMenu>,
);

cy.contains('5').should('be.visible');
});

it('does not show count badge when count is 0', () => {
cy.mount(
<DropdownMenu label='Test Menu' icon={mockIcon} count={0} testMode={true}>
{mockChildren}
</DropdownMenu>,
);

cy.contains('0').should('not.exist');
});

it('rotates arrow icon when dropdown is toggled', () => {
cy.mount(
<DropdownMenu label='Test Menu' icon={mockIcon} testMode={true}>
{mockChildren}
</DropdownMenu>,
);

// Initially arrow should point down
cy.get('#arrow')
.should('have.attr', 'style')
.and('include', 'rotate(0deg)');

// Click to open
cy.get('button').click();
cy.get('#arrow')
.should('have.attr', 'style')
.and('include', 'rotate(180deg)');

// Click to close
cy.get('button').click();
cy.get('#arrow')
.should('have.attr', 'style')
.and('include', 'rotate(0deg)');
});

it('toggles content visibility multiple times', () => {
cy.mount(
<DropdownMenu label='Test Menu' icon={mockIcon} testMode={true}>
{mockChildren}
</DropdownMenu>,
);

// First toggle
cy.get('button').click();
cy.get('[data-testid="test-content"]').should('be.visible');

// Second toggle
cy.get('button').click();
cy.get('[data-testid="test-content"]').should('not.exist');

// Third toggle
cy.get('button').click();
cy.get('[data-testid="test-content"]').should('be.visible');
});
});
95 changes: 95 additions & 0 deletions cypress/components/Radio.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react';
import Radio from '@/pages/tools/components/ui/Radio';

describe('Radio Component', () => {
it('renders with label and value', () => {
cy.mount(
<Radio
label='Test Radio'
value='test'
selectedValue=''
onChange={() => {}}
/>,
);

cy.get('[role="radiogroup"]').should('exist');
cy.get('[role="radio"]').should('exist');
cy.contains('Test Radio').should('be.visible');
});

it('handles selection change', () => {
const onChangeSpy = cy.spy().as('onChangeSpy');

cy.mount(
<Radio
label='Test Radio'
value='test'
selectedValue=''
onChange={onChangeSpy}
/>,
);

cy.get('[role="radio"]').click();
cy.get('@onChangeSpy').should('have.been.calledWith', 'test');
});

it('shows correct selected state', () => {
cy.mount(
<Radio
label='Test Radio'
value='test'
selectedValue='test'
onChange={() => {}}
/>,
);

cy.get('[role="radio"]').should('have.attr', 'data-state', 'checked');
});

it('has correct styling classes', () => {
cy.mount(
<Radio
label='Test Radio'
value='test'
selectedValue=''
onChange={() => {}}
/>,
);

cy.get('label').should('have.class', 'flex');
cy.get('label').should('have.class', 'items-center');
cy.get('label').should('have.class', 'gap-3');
cy.get('label').should('have.class', 'px-4');
cy.get('label').should('have.class', 'py-2');
});

it('maintains selection state when re-rendered', () => {
const onChangeSpy = cy.spy().as('onChangeSpy');

cy.mount(
<Radio
label='Test Radio'
value='test'
selectedValue=''
onChange={onChangeSpy}
/>,
);

// First click to select
cy.get('[role="radio"]').click();
cy.get('@onChangeSpy').should('have.been.calledWith', 'test');

// Re-mount with the selected value
cy.mount(
<Radio
label='Test Radio'
value='test'
selectedValue='test'
onChange={onChangeSpy}
/>,
);

// Verify the selection state is maintained
cy.get('[role="radio"]').should('have.attr', 'data-state', 'checked');
});
});
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"dependencies": {
"@docsearch/react": "3.9.0",
"@radix-ui/react-checkbox": "^1.3.1",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-radio-group": "^1.3.7",
"@radix-ui/react-slot": "^1.2.2",
"@types/jsonpath": "^0.2.4",
"axios": "1.9.0",
"babel-loader": "^9.2.1",
Expand Down Expand Up @@ -59,7 +61,6 @@
"slugify": "^1.6.5",
"tailwind-merge": "^3.3.0",
"tw-animate-css": "^1.2.9",
"yarn": "1.22.22",
"zero-fill": "^2.2.4",
"zustand": "^5.0.0"
},
Expand Down
Loading