Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "iconland"]
path = iconland
url = git@github.com:Newton-School/iconland.git
url = https://github.com/Newton-School/iconland.git
2 changes: 1 addition & 1 deletion ui/css/index.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@import "reset";
@import "grauity-icons";
// @import "grauity-icons";
18 changes: 12 additions & 6 deletions ui/elements/Accordion/Accordion.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '@testing-library/jest-dom';

import { fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import React from 'react';

import Accordion from './Accordion';
Expand All @@ -19,19 +19,25 @@ describe('Accordion Component', () => {
);
expect(screen.getByText('Test Content')).toBeInTheDocument();
});
it('renders the correct children when expanded is false', () => {
it('renders the correct children when expanded is false', async () => {
render(<Accordion title="Test Title">Test Content</Accordion>);
expect(screen.queryByText('Test Content')).not.toBeInTheDocument();
await waitFor(()=>{
expect(screen.queryByText('Test Content')).not.toBeInTheDocument();
});
});

it('toggles expanded state when clicked', async () => {
render(<Accordion title="Test Title">Test Content</Accordion>);
const titleElement = screen.getByText('Test Title');
fireEvent.click(titleElement);
expect(screen.getByText('Test Content')).toBeInTheDocument();
await waitFor(()=>{
expect(screen.getByText('Test Content')).toBeInTheDocument();
});
fireEvent.click(titleElement);
await new Promise((r) => setTimeout(r, 3000));
expect(screen.queryByText('Test Content')).not.toBeInTheDocument();
// await new Promise((r) => setTimeout(r, 3000));
await waitFor(()=>{
expect(screen.queryByText('Test Content')).not.toBeInTheDocument();
});
}, 5000);

it('calls onToggle when expanded state changes', () => {
Expand Down
15 changes: 10 additions & 5 deletions ui/elements/BottomSheet/BottomSheet.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '@testing-library/jest-dom';

import { fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import React from 'react';

import Button from '../Button';
Expand All @@ -26,13 +26,18 @@ const TestBottomSheet = (props: BottomSheetProps) => {

describe('BottomSheet Component', () => {
// Rendering
it('does not render initially', () => {
it('does not render initially', async () => {
render(<TestBottomSheet />);
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
await waitFor(()=>{
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
});
it('renders on pressing the button', () => {

it('renders on pressing the button', async () => {
render(<TestBottomSheet />);
fireEvent.click(screen.getByText('Open BottomSheet'));
expect(screen.getByRole('dialog')).toBeInTheDocument();
await waitFor(()=>{
expect(screen.getByRole('dialog')).toBeInTheDocument();
});
});
});
8 changes: 6 additions & 2 deletions ui/elements/Carousel/Carousel.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ export const StyledCarouselItemsContainer = styled.div<StyledCarouselItemsContai
width: 100%;
display: flex;
gap: ${(props) => props.$gap}px;
overflow-x: auto;
scroll-behavior: smooth;
transform: translateX(${(props) => props.$translateX}px);
transition: transform 0.5s ease-in-out;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
`;
export const StyledCarouselItem = styled.div<StyledCarouselItemProps>`
flex: 0 0 auto;
Expand Down
88 changes: 54 additions & 34 deletions ui/elements/Carousel/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,53 +34,74 @@ const Carousel = (props: CarouselProps) => {
const headerRef = useRef<HTMLDivElement | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null);

const [translateX, setTranslateX] = useState(0);
// const [translateX, setTranslateX] = useState(0);

Copilot AI May 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] If the translateX state is no longer used, consider removing this commented-out code for clarity.

Suggested change
// const [translateX, setTranslateX] = useState(0);

Copilot uses AI. Check for mistakes.
const [leftButtonDisabled, setLeftButtonDisabled] = useState(false);
const [rightButtonDisabled, setRightButtonDisabled] = useState(false);
const [showIcons, setShowIcons] = useState(true);

const handleControlClick = (direction: 'left' | 'right') => {
const scrollableWidth = containerRef.current?.scrollWidth;
const visibleWidth = containerRef.current?.clientWidth;

const headerLeft = headerRef.current?.getBoundingClientRect().left;
const containerLeft =
containerRef.current?.getBoundingClientRect().left;
const currentLeft = containerLeft - headerLeft;

const actualScrollAmount = fullWidthItems
? visibleWidth + gap
const container = containerRef.current;
if (!container) return;

// Determine how much to scroll
const scrollAmountPixels = fullWidthItems
? container.clientWidth + gap
: scrollAmount;

// Set scroll direction
const scrollValue = direction === 'left' ? -scrollAmountPixels : scrollAmountPixels;

console.log('Button clicked:', direction, 'scrollValue:', scrollValue);

// Trigger native smooth scrolling
container.scrollBy({
left: scrollValue,
behavior: 'smooth',
});

// Call respective callback
if (direction === 'left') {
const newLeft = Math.min(0, currentLeft + actualScrollAmount);
setTranslateX(newLeft);
onLeftClick();
} else if (direction === 'right') {
const newLeft = Math.max(
-scrollableWidth + visibleWidth,
currentLeft - actualScrollAmount
);
setTranslateX(newLeft);
} else {
onRightClick();
}
};


useEffect(() => {
setLeftButtonDisabled(translateX === 0);
const scrolledToEnd =
translateX <=
containerRef.current?.clientWidth -
containerRef.current?.scrollWidth;
setRightButtonDisabled(scrolledToEnd);
if (scrolledToEnd) {
onScrollEnd();
}
}, [
translateX,
containerRef.current?.clientWidth,
containerRef.current?.scrollWidth,
]);
const container = containerRef.current;
if (!container) return;

const handleScroll = () => {
const scrollLeft = container.scrollLeft;
const scrollWidth = container.scrollWidth;
const clientWidth = container.clientWidth;

const atStart = scrollLeft <= 0;
const atEnd = scrollLeft + clientWidth >= scrollWidth - 1;

console.log('Scroll event:', {scrollLeft, scrollWidth, clientWidth, atStart, atEnd});

setLeftButtonDisabled(atStart);
setRightButtonDisabled(atEnd);

if (atEnd) {
onScrollEnd();
}
};

// Attach scroll listener
container.addEventListener('scroll', handleScroll);

// Initial check
handleScroll();

// Cleanup
return () => {
container.removeEventListener('scroll', handleScroll);
};
}, [onScrollEnd]);


useEffect(() => {
if (hideIconsOnLessItems) {
Expand Down Expand Up @@ -143,7 +164,6 @@ const Carousel = (props: CarouselProps) => {
<StyledCarouselItemsContainer
ref={containerRef}
$gap={gap}
$translateX={translateX}
>
{items.map((item) => (
<StyledCarouselItem $fullWidth={fullWidthItems}>
Expand Down
2 changes: 1 addition & 1 deletion ui/elements/Carousel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export interface StyledCarouselControlsProps extends StyledDivProps {

export interface StyledCarouselItemsContainerProps extends StyledDivProps {
$gap: number;
$translateX: number;
// $translateX: number;
}

export interface StyledCarouselItemProps extends StyledDivProps {
Expand Down
57 changes: 39 additions & 18 deletions ui/elements/Form/Dropdown/Dropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '@testing-library/jest-dom';

import { fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import React from 'react';

import { BaseItemOptionProps, BaseItemType } from '../../DropdownMenu';
Expand Down Expand Up @@ -64,7 +64,7 @@ describe('Dropdown', () => {
});

// Single Select Mode Flow
it('Should run entire flow correctly in single select mode if no action buttons', () => {
it('Should run entire flow correctly in single select mode if no action buttons', async () => {
const onChange = jest.fn();
const items = getDummyOptions(3);

Expand All @@ -86,15 +86,18 @@ describe('Dropdown', () => {
expect(onChange).toHaveBeenCalledWith(items[0]);

// Should close the dropdown
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
await waitFor(()=>{
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
});


// Should call onChange on selecting another item
fireEvent.click(screen.getByText('Select'));
fireEvent.click(screen.getByText('Item 1'));
expect(onChange).toHaveBeenCalledWith(items[1]);
expect(screen.queryByText('Item 1')).not.toBeInTheDocument();
});
it('Should run entire flow correctly in single select mode if action buttons are present', () => {
it('Should run entire flow correctly in single select mode if action buttons are present', async () => {
const onChange = jest.fn();
const items = getDummyOptions(3);

Expand All @@ -119,13 +122,16 @@ describe('Dropdown', () => {
// Should call onChange on clicking apply button
fireEvent.click(screen.getByText('Apply'));
expect(onChange).toHaveBeenCalledWith(items[0]);

// Should close the dropdown
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
await waitFor(()=>{
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
});

});

// Multi Select Mode Flow
it('Should run entire flow correctly in multi select mode if no action buttons', () => {
it('Should run entire flow correctly in multi select mode if no action buttons', async () => {
const onChange = jest.fn();
const items = getDummyOptions(3);

Expand Down Expand Up @@ -159,12 +165,18 @@ describe('Dropdown', () => {

// Should call onChange on clicking outside the dropdown
fireEvent.mouseDown(document.body);
expect(onChange).toHaveBeenCalledWith([items[0], items[1]]);
await waitFor(()=>{
expect(onChange).toHaveBeenCalledWith([items[0], items[1]]);
});


// Should close the dropdown
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
await waitFor(()=>{
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
});

});
it('Should run entire flow correctly in multi select mode if action buttons are present', () => {
it('Should run entire flow correctly in multi select mode if action buttons are present', async () => {
const onChange = jest.fn();
const items = getDummyOptions(3);

Expand Down Expand Up @@ -196,15 +208,18 @@ describe('Dropdown', () => {
});
expect(selectedItems).toHaveLength(2);
expect(onChange).not.toHaveBeenCalled();

// Should not call onChange on clicking outside the dropdown
fireEvent.mouseDown(document.body);
expect(onChange).not.toHaveBeenCalled();

// Should call onChange on clicking apply button
fireEvent.click(screen.getByText('Apply'));
expect(onChange).toHaveBeenCalledWith([items[0], items[1]]);
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
await waitFor(()=>{
expect(onChange).toHaveBeenCalledWith([items[0], items[1]]);
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
});


// Should clear the selected items on clicking clear button
fireEvent.click(screen.getByText('Select'));
Expand All @@ -220,8 +235,11 @@ describe('Dropdown', () => {
});
expect(selectedItems).toHaveLength(0);
fireEvent.click(screen.getByText('Apply'));
expect(onChange).toHaveBeenCalledWith([]);
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
await waitFor(()=>{
expect(onChange).toHaveBeenCalledWith([]);
expect(screen.queryByText('Item 0')).not.toBeInTheDocument();
});

});

// Show Selected Value
Expand All @@ -245,7 +263,7 @@ describe('Dropdown', () => {
expect(screen.queryByText('Item 0')).toBeInTheDocument();
expect(screen.queryByText('Select')).not.toBeInTheDocument();
});
it('Should show selected value on trigger in multi select mode', () => {
it('Should show selected value on trigger in multi select mode', async () => {
const items = getDummyOptions(3);

render(
Expand All @@ -265,8 +283,11 @@ describe('Dropdown', () => {
fireEvent.mouseDown(document.body);

// Should show the selected value and not placeholder
expect(screen.queryByText('Select')).not.toBeInTheDocument();
expect(screen.queryByText('2 selected')).toBeInTheDocument();
await waitFor(()=>{
expect(screen.queryByText('Select')).not.toBeInTheDocument();
expect(screen.queryByText('2 selected')).toBeInTheDocument();
});

});

// Custom Trigger
Expand Down
Loading