Skip to content
Merged
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
5 changes: 4 additions & 1 deletion __tests__/components/waves/Waves.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ jest.mock("@/components/waves/list/WavesList", () => {

const { useSearchParams } = jest.requireMock("next/navigation");
const { default: useDeviceInfo } = require("@/hooks/useDeviceInfo");
const deviceInfoMock = useDeviceInfo as jest.Mock;

const mockRouter = { push: jest.fn(), replace: jest.fn() };

beforeEach(() => {
mockRouter.push.mockReset();
mockRouter.replace.mockReset();
(useRouter as jest.Mock).mockReturnValue(mockRouter);
deviceInfoMock.mockReturnValue({ isApp: false });
});

const baseAuth = {
Expand Down Expand Up @@ -82,11 +84,13 @@ function renderWaves(params: Map<string, string | null>) {
}

it("shows CreateWave when ?create=wave is present", () => {
deviceInfoMock.mockReturnValue({ isApp: true });
renderWaves(new Map([["create", "wave"]]));
expect(screen.getByTestId("create-wave")).toBeInTheDocument();
});

it("shows CreateDM when ?create=dm is present", () => {
deviceInfoMock.mockReturnValue({ isApp: true });
renderWaves(new Map([["create", "dm"]]));
expect(screen.getByTestId("create-dm")).toBeInTheDocument();
});
Expand All @@ -110,7 +114,6 @@ it("navigates on button clicks", async () => {
});

it("navigates to app create routes when running in app", async () => {
const deviceInfoMock = useDeviceInfo as jest.Mock;
deviceInfoMock.mockReturnValue({ isApp: true });

const user = userEvent.setup();
Expand Down
50 changes: 47 additions & 3 deletions __tests__/components/waves/list/WaveItem.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { render, screen } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import WaveItem from '@/components/waves/list/WaveItem';

jest.mock('@/components/waves/list/WaveItemDropped', () => () => <div data-testid="dropped" />);
jest.mock('@/components/waves/list/WaveItemFollow', () => () => <div data-testid="follow" />);
const push = jest.fn();

jest.mock('next/navigation', () => ({
useRouter: () => ({
push,
}),
}));

jest.mock('@/components/waves/list/WaveItemDropped', () => () => <a data-testid="dropped" href="#dropped">Dropped</a>);
jest.mock('@/components/waves/list/WaveItemFollow', () => () => (
<button data-testid="follow" type="button">
Follow
</button>
));

const wave = {
id: 'w1',
Expand All @@ -21,6 +33,10 @@ const wave = {
} as any;

describe('WaveItem', () => {
beforeEach(() => {
push.mockClear();
});

it('renders wave data', () => {
render(<WaveItem wave={wave} />);
expect(screen.getByText('My Wave')).toBeInTheDocument();
Expand All @@ -35,4 +51,32 @@ describe('WaveItem', () => {
expect(screen.getByText('user')).toBeInTheDocument();
expect(screen.getByText('title')).toBeInTheDocument();
});

it('navigates when the card surface is clicked', () => {
render(<WaveItem wave={wave} />);
fireEvent.click(screen.getByLabelText('View wave My Wave'));
expect(push).toHaveBeenCalledWith('/waves?wave=w1');
});

it('navigates when pressing Enter while the card is focused', () => {
render(<WaveItem wave={wave} />);
const card = screen.getByLabelText('View wave My Wave');
card.focus();
fireEvent.keyDown(card, { key: 'Enter' });
expect(push).toHaveBeenCalledWith('/waves?wave=w1');
});

it('navigates when pressing Space while the card is focused', () => {
render(<WaveItem wave={wave} />);
const card = screen.getByLabelText('View wave My Wave');
card.focus();
fireEvent.keyDown(card, { key: ' ' });
expect(push).toHaveBeenCalledWith('/waves?wave=w1');
});

it('does not navigate when clicking on the follow button', () => {
render(<WaveItem wave={wave} />);
fireEvent.click(screen.getByTestId('follow'));
expect(push).not.toHaveBeenCalled();
});
});
7 changes: 4 additions & 3 deletions __tests__/components/waves/list/WaveItemDropped.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ const wave: any = {
]
};

test('renders first five contributor avatars and plural text', () => {
test('renders all contributor avatars and plural text', () => {
render(<WaveItemDropped wave={wave} />);
const imgs = screen.getAllByRole('img');
expect(imgs).toHaveLength(5); // only first five
expect(imgs).toHaveLength(wave.contributors_overview.length);
expect(imgs[0]).toHaveAttribute('src', 'scaled-a.png');
expect(imgs.at(-1)).toHaveAttribute('src', 'scaled-f.png');
expect(screen.getByText('2')).toBeInTheDocument();
expect(screen.getByText('Drops')).toBeInTheDocument();
});

test('uses singular label when one drop', () => {
const single = { ...wave, metrics: { drops_count: 1 } } as any;
const single = { ...wave, metrics: { drops_count: 1 } };
render(<WaveItemDropped wave={single} />);
expect(screen.getByText('Drop')).toBeInTheDocument();
});
2 changes: 2 additions & 0 deletions codex/STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ This table is the single source of truth for active and historical tickets. Keep
| TKT-0014 | Replace wave publish wait with backend confirmation | Backlog | P1 | openai-assistant | — | 2025-10-24 |
| TKT-0015 | Unify header search results | In-Progress | P1 | openai-assistant | [#1567](https://github.com/6529-Collections/6529seize-frontend/pull/1567) | 2025-10-24 |
| TKT-0016 | Upgrade Next.js app to version 16 | In-Progress | P0 | openai-assistant | — | 2025-10-27 |
| TKT-0017 | Stabilize Waves modal tests for app routing | In-Progress | P1 | openai-assistant | — | 2025-10-27 |
| TKT-0018 | Make Wave card fully clickable | In-Progress | P1 | openai-assistant | — | 2025-10-27 |

## Usage Guidelines

Expand Down
1 change: 1 addition & 0 deletions codex/tickets/TKT-0011.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ The identity picker modal opens a typeahead list, but arrow key navigation has r
- 2025-10-26T11:55:18Z – Replaced the nested button with a clickable list item so Sonar stops flagging duplicated roles while preserving keyboard activation.
- 2025-10-26T12:05:09Z – Added an explicit `role="listbox"` to the profile search results container so the custom `<li role="option">` elements meet the WAI-ARIA combobox contract and silence the reopened Sonar warning.
- 2025-10-26T12:20:00Z – Manually re-ran the keyboard navigation checks, confirmed all acceptance criteria, and documented the pending PR linkage for `block-add-identity-to-wave`.

2 changes: 0 additions & 2 deletions codex/tickets/TKT-0016.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ status: In-Progress
title: Upgrade Next.js app to version 16
---

## Context

> Upgrade the frontend to Next.js 16 using the provided Next Devtools automation so we can adopt the latest React 19 runtime and platform tooling.

## Plan
Expand Down
36 changes: 36 additions & 0 deletions codex/tickets/TKT-0017.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
created: 2025-10-27
id: TKT-0017
owner: openai-assistant
priority: P1
status: In-Progress
title: Stabilize Waves modal tests for app routing
---

## Context

> Recent regressions in the Waves create entrypoints caused our Jest suite to expect elements that only render when the app shell reports `isApp = true`. The existing tests mocked the device state inconsistently, so the suite flapped whenever other specs imported `useDeviceInfo`. We need a focused ticket to realign the test harness with production behaviour and keep the modal coverage reliable.

## Plan

- [x] Inspect the current Waves tests and identify coupling to the device info hook.
- [x] Adjust the mocks so each scenario explicitly sets the app context required by the assertions.
- [ ] Add regression coverage to ensure the create modal expectations hold without relying on global mock state.
- [ ] Run targeted Jest suites and capture results in the log.

## Acceptance

- [ ] `__tests__/components/waves/Waves.test.tsx` passes deterministically regardless of other suites.
- [ ] Modal visibility assertions are scoped to the app-mode rendering path.
- [ ] `npm run test -- Waves` succeeds without modifying production code.

## Links

- Primary PR: _(pending)_
- Follow-ups: _(none yet)_

## Log

- 2025-10-27T14:10:00Z – Documented the flakey Waves tests and logged the need for device info scoping before fixing the mocks.
Comment thread
ragnep marked this conversation as resolved.
- 2025-10-27T14:45:00Z – Updated the Waves and WaveItemDropped specs to control the device mock explicitly and align avatar expectations with the production component; targeted Jest runs pass aside from coverage reporter sandbox noise.

52 changes: 52 additions & 0 deletions codex/tickets/TKT-0018.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
created: 2025-10-27
id: TKT-0018
owner: openai-assistant
priority: P1
status: In-Progress
title: Make Wave card fully clickable
---

## Context

@waveitem reported that the Wave card currently exposes an eye icon button for navigation, but the expectation is that the entire card surface should link to the wave. This change keeps the UX consistent with the rest of the interface and removes redundant affordances.

## Plan

- [x] Review the current Wave card interactions and identify all navigation entry points.
- [x] Update the card to handle click and keyboard events across its surface without breaking nested controls.
- [x] Adjust and extend tests to cover the new interactions.

## Acceptance

- [x] Clicking any non-interactive area of a Wave card navigates to the wave detail view.
- [x] Existing interactive elements (like follow actions and contributor links) continue to work without unintended navigation.
- [x] Automated tests cover the navigation behavior.

## Links

- Primary PR: _(add when available)_
- Follow-ups: _(reference additional tickets or TODO items)_

## Log

- 2025-10-27T09:37:57Z – Captured request to remove the eye icon and make the card surface navigable.
- 2025-10-27T09:45:00Z – Wired full-card navigation, removed the legacy eye icon, and added interaction coverage.
- 2025-10-27T09:58:50Z – Polished card styling with hover/contrast refinements while keeping nested controls intact.
- 2025-10-27T10:14:47Z – Inlined Tailwind surface styling on the Wave card JSX, retained guarded navigation handlers, and added a space-key interaction test (`npm run test -- --runTestsByPath __tests__/components/waves/list/WaveItem.test.tsx`).
- 2025-10-27T10:32:05Z – Hardened Wave card links, tooltips, and images (prefetch opt-out, optional chaining, memoized gradients, lazy-loading avatars) per @waveitem’s follow-up; no new tests executed per guidance.
- 2025-10-27T10:34:45Z – Rolled back the new memoization helpers to keep the card implementation straightforward while retaining the other hardening tweaks; tests still not run.
- 2025-10-27T10:36:37Z – Adjusted the subscribers row to remain a flex container at large breakpoints so the icon/count stay vertically centered.
- 2025-10-27T10:37:17Z – Replaced inline SVGs with `@heroicons/react/24/outline` icons for chat and subscriber rows per styling request.
- 2025-10-27T10:39:25Z – Swapped the subscriber icon to `UsersIcon` to match the “two user” visual preference.
- 2025-10-27T10:40:37Z – Centered the author row by flex-aligning the container/link so avatar, handle, and level pill stay vertically aligned.
- 2025-10-27T10:41:59Z – Centered drop contributor avatars/count by aligning both rows with flex utilities.
- 2025-10-27T11:27:47Z – Increased Wave card and contributor avatar image scales to `AUTOx450` / `200x200` for sharper previews.
- 2025-10-27T11:33:06Z – Re-applied the higher-resolution Wave hero/author image scales after the Card layout tweaks.
- 2025-10-27T11:34:14Z – Tightened the hero overlay to a bottom-heavy gradient so the text block stays readable without dimming the whole image.
- 2025-10-27T11:43:58Z – Boosted wave hero, author, and contributor image requests to a new `250x250` scale for sharper rendering.
- 2025-10-27T11:46:17Z – Scoped the `250x250` scale to the wave hero only and restored author/contributor avatars to their lighter `AUTOx50` variant per request.
- 2025-10-27T11:47:13Z – Reverted the hero image to `AUTOx450`; CDN lacks a `250x250` variant so the asset vanished.
- 2025-10-27T12:09:23Z – Retried `250x250` for the hero (keeping avatars on `AUTOx50`) per latest request.
- 2025-10-27T12:12:45Z – Restored the hero image scale to `AUTOx450` to ensure the CDN-served asset renders reliably.
- 2025-10-27T14:06:24Z – Moved the Wave avatar/handle/level row below the hero image and above the meta bar to match the requested layout.
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ const WebDirectMessagesList: React.FC<WebDirectMessagesListProps> = ({
onClicked={openDirectMessage}
loading={false}
disabled={false}
padding="tw-px-2 tw-py-2"
padding="tw-p-2.5"
>
<FontAwesomeIcon
icon={faPaperPlane}
Expand Down
10 changes: 5 additions & 5 deletions components/brain/left-sidebar/web/WebUnifiedWavesListWaves.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const WebUnifiedWavesListWaves = forwardRef<
sentinelRef,
}));

const shouldRenderCreateWaveButton = !isApp && !!connectedProfile;
const showCreateWaveButton = !isApp && !!connectedProfile;

const { pinnedWaves, regularWaves } = useMemo(() => {
const pinned: MinimalWave[] = [];
Expand Down Expand Up @@ -129,7 +129,7 @@ const WebUnifiedWavesListWaves = forwardRef<
<div className="tw-flex tw-flex-col">
{!hideHeaders &&
(isCollapsed ? (
shouldRenderCreateWaveButton && (
showCreateWaveButton && (
<div className="tw-flex tw-justify-center tw-px-2 tw-mb-3.5">
<div
data-tooltip-id="create-wave-tooltip"
Expand All @@ -139,7 +139,7 @@ const WebUnifiedWavesListWaves = forwardRef<
onClicked={openWave}
loading={false}
disabled={false}
padding="tw-px-2 tw-py-2"
padding="tw-p-2.5"
>
<FontAwesomeIcon
icon={faPlus}
Expand All @@ -153,7 +153,7 @@ const WebUnifiedWavesListWaves = forwardRef<
<SectionHeader
label="Waves"
rightContent={
shouldRenderCreateWaveButton ? (
showCreateWaveButton ? (
<div
data-tooltip-id="create-wave-tooltip"
data-tooltip-content="Create wave"
Expand All @@ -162,7 +162,7 @@ const WebUnifiedWavesListWaves = forwardRef<
onClicked={openWave}
loading={false}
disabled={false}
padding="tw-px-2 tw-py-2"
padding="tw-p-2.5"
>
<FontAwesomeIcon
icon={faPlus}
Expand Down
3 changes: 1 addition & 2 deletions components/messages/MessagesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ const MessagesView: React.FC = () => {
onClicked={openDirectMessage}
loading={false}
disabled={false}
padding="tw-px-4 tw-py-2"
>
<FontAwesomeIcon
icon={faPaperPlane}
className="tw-size-4 tw-flex-shrink-0 tw-mr-2"
/>
<span>New Direct Message</span>
<span>Create DM</span>
</PrimaryButton>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion components/user/waves/UserPageWavesSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default function UserPageWavesSearch({
disabled={false}>
<FontAwesomeIcon
icon={faPaperPlane}
className="tw-size-4 tw-mr-1.5 -tw-ml-1.5 tw-flex-shrink-0"
className="tw-size-4 tw-mr-2 -tw-ml-1 tw-flex-shrink-0"
/>
<span>Create DM</span>
</PrimaryButton>
Expand Down
2 changes: 1 addition & 1 deletion components/waves/drops/ArtistActiveSubmissionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const ArtistActiveSubmissionContent: React.FC<
className={`tw-relative tw-z-[100] tw-p-6 ${
isApp
? ""
: "tw-overflow-y-auto tw-scrollbar-thin tw-scrollbar-thumb-iron-500 tw-scrollbar-track-iron-800 hover:tw-scrollbar-thumb-iron-300 tw-max-h-[calc(75vh-120px)] sm:tw-max-h-[calc(80vh-120px)]"
: "tw-overflow-y-auto tw-scrollbar-thin tw-scrollbar-thumb-iron-500 tw-scrollbar-track-iron-800 hover:tw-scrollbar-thumb-iron-300 tw-max-h-[calc(75vh-120px)] sm:tw-max-h-[calc(90vh-140px)]"
Comment thread
ragnep marked this conversation as resolved.
}`}
>
{(() => {
Expand Down
2 changes: 1 addition & 1 deletion components/waves/drops/ArtistWinningArtworksContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const ArtistWinningArtworksContent: React.FC<

return (
<div
className={`tw-relative tw-z-[100] tw-p-6 tw-overflow-y-auto tw-scrollbar-thin tw-scrollbar-thumb-iron-500 tw-scrollbar-track-iron-800 hover:tw-scrollbar-thumb-iron-300 tw-max-h-[calc(75vh-120px)] sm:tw-max-h-[calc(80vh-120px)]`}
className={`tw-relative tw-z-[100] tw-p-6 tw-overflow-y-auto tw-scrollbar-thin tw-scrollbar-thumb-iron-500 tw-scrollbar-track-iron-800 hover:tw-scrollbar-thumb-iron-300 tw-max-h-[calc(75vh-120px)] sm:tw-max-h-[calc(90vh-140px)]`}
Comment thread
ragnep marked this conversation as resolved.
>
<div className="tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 lg:tw-grid-cols-3 tw-gap-6">
{winningDrops.map((drop) => {
Expand Down
Loading