jest issue. about to try to fix https://github.com/nanostores/nanostores/issues/210
This commit is contained in:
40
src/components/LibraryItem.test.tsx
Normal file
40
src/components/LibraryItem.test.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { expect, test } from 'jest-without-globals';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { LibraryItem } from './LibraryItem';
|
||||
import { Category, LibraryItem as LibItem } from '../lib/prompt';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
|
||||
const mockOnAddItem = jest.fn();
|
||||
|
||||
const mockItem: LibItem = {
|
||||
id: randomUUID(),
|
||||
name: 'Test Item',
|
||||
category: Category.medium,
|
||||
prompt: 'Test Prompt',
|
||||
};
|
||||
|
||||
test('renders library item with add button', () => {
|
||||
render(<LibraryItem item={mockItem} onAddItem={mockOnAddItem} />);
|
||||
|
||||
const addButton = screen.getByLabelText('Add');
|
||||
const itemName = screen.getByText((content, element) => {
|
||||
return content.includes(mockItem.name as string);
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
expect(addButton).toBeInTheDocument();
|
||||
// @ts-ignore
|
||||
expect(itemName).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('calls onAddItem when add button is clicked', async () => {
|
||||
render(<LibraryItem item={mockItem} onAddItem={mockOnAddItem} />);
|
||||
|
||||
const addButton = screen.getByLabelText('Add');
|
||||
|
||||
userEvent.click(addButton);
|
||||
|
||||
expect(mockOnAddItem).toHaveBeenCalledWith(mockItem);
|
||||
});
|
@ -5,14 +5,14 @@ import { Button } from "@material-ui/core";
|
||||
|
||||
export interface StyleProps {
|
||||
item: LibItem
|
||||
onAddItem: (item: LibItem) => void;
|
||||
onInsertItem: (item: LibItem) => void;
|
||||
}
|
||||
|
||||
export function LibraryItem(props: StyleProps) {
|
||||
const { item, onAddItem } = props
|
||||
const { item, onInsertItem } = props
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={() => onAddItem(item)}>
|
||||
<Button onClick={() => onInsertItem(item)} aria-label="Add">
|
||||
<AddCircleOutlineOutlinedIcon/>
|
||||
</Button>
|
||||
<span>
|
||||
|
@ -5,20 +5,28 @@ import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import {Composable} from "./IComposable"
|
||||
|
||||
import "./Nugget.css";
|
||||
import { Nugget as NuggetType, decreaseNuggetScore, increaseNuggetScore } from '../lib/prompt';
|
||||
|
||||
export default function Nugget({ text, initialScore }: { text: string, initialScore?: number }) {
|
||||
const [score, setScore] = useState(initialScore || 0);
|
||||
export interface NuggetProps {
|
||||
nugget : NuggetType,
|
||||
}
|
||||
|
||||
export default function Nugget(props : NuggetProps) {
|
||||
const {nugget} = props;
|
||||
|
||||
const scoreDisp = nugget.score > 0 ? "+" + nugget.score : nugget.score;
|
||||
|
||||
return (
|
||||
<div className='nugget'>
|
||||
<span className='text'>{text}</span>
|
||||
<span className='text'>{nugget.item.name || nugget.item.prompt}</span>
|
||||
<Divider orientation="vertical" variant="middle" flexItem />
|
||||
<span className='score'>{score > 0 ? "+" + score : score}</span>
|
||||
<span className='score'>{scoreDisp}</span>
|
||||
<span className='buttons'>
|
||||
<ButtonGroup size="small" orientation='vertical'>
|
||||
<Button onClick={() => setScore(score + 1)} className='incScore'>
|
||||
<Button onClick={() => increaseNuggetScore(nugget.id)} className='incScore'>
|
||||
<KeyboardArrowUpIcon />
|
||||
</Button>
|
||||
<Button onClick={() => setScore(score - 1)} className='decScore'>
|
||||
<Button onClick={() => decreaseNuggetScore(nugget.id)} className='decScore'>
|
||||
<KeyboardArrowDownIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
@ -4,10 +4,16 @@ import "./Operation.css";
|
||||
import { Op } from "../lib/operator";
|
||||
import { randomUUID } from "crypto";
|
||||
import { Composable } from "./IComposable";
|
||||
import { Operation as OperationType, changeOperationOp } from "../lib/prompt";
|
||||
import Nugget from "./Nugget";
|
||||
|
||||
interface OperationProps {
|
||||
operation : OperationType
|
||||
}
|
||||
|
||||
function Operation(props : OperationProps) {
|
||||
const {operation} = props;
|
||||
|
||||
function Operation({ children, initialOp }: { children: ReactNode[], initialOp: Op }) {
|
||||
const [op, setOp] = React.useState(initialOp);
|
||||
const [contextMenu, setContextMenu] = React.useState<{
|
||||
mouseX: number;
|
||||
mouseY: number;
|
||||
@ -35,19 +41,19 @@ function Operation({ children, initialOp }: { children: ReactNode[], initialOp:
|
||||
};
|
||||
|
||||
const changeOperator = (opV: string) => {
|
||||
setOp(opV as unknown as Op);
|
||||
changeOperationOp(operation.id, opV as Op);
|
||||
handleClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="operation" onContextMenu={handleContextMenu}>
|
||||
<div className="title">{op}</div>
|
||||
<div className="title">{operation.op}</div>
|
||||
<div className="nuggets">
|
||||
{Children.map(children, child => {
|
||||
return (<div>
|
||||
{child}
|
||||
</div>)
|
||||
})}
|
||||
{
|
||||
operation.items.map(nugget => {
|
||||
return <Nugget nugget={nugget} />
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<Menu
|
||||
open={contextMenu !== null}
|
||||
|
@ -1,16 +1,19 @@
|
||||
import { Button } from '@material-ui/core';
|
||||
import { Children, ReactNode } from 'react';
|
||||
import Masonry from '@mui/lab/Masonry';
|
||||
import Nugget from './Nugget';
|
||||
import { Operation } from './Operation';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import "./PromptArea.css";
|
||||
import { PromptLibrary } from './PromptLibrary';
|
||||
import React from 'react';
|
||||
import { $composition, $slottedComposition, LibraryItem, insertIntoComposition } from '../lib/prompt';
|
||||
import { Category } from '@mui/icons-material';
|
||||
import { useStore } from '@nanostores/react'
|
||||
|
||||
type Composable = (typeof Nugget) | (typeof Operation);
|
||||
|
||||
export default function PromptArea({ children }: { children?: any }) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setOpen(true);
|
||||
@ -21,29 +24,27 @@ export default function PromptArea({ children }: { children?: any }) {
|
||||
// setSelectedValue(value);
|
||||
};
|
||||
|
||||
const handleComposableClicked = (composable : Composable) => {
|
||||
setSelectedComposable(composable);
|
||||
const handleOnInsertItem = (item: LibraryItem) => {
|
||||
insertIntoComposition(item);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button className="add-button">
|
||||
<AddIcon />
|
||||
<PromptLibrary
|
||||
open={isPromptLibraryOpen}
|
||||
onClose={handleClose}
|
||||
></PromptLibrary>
|
||||
const slottedComposition = useStore($slottedComposition);
|
||||
|
||||
</Button>
|
||||
<div>
|
||||
{
|
||||
Children.map(children, child => {
|
||||
return (<div>
|
||||
{child as ReactNode}
|
||||
</div>)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<Button className="add-button">
|
||||
<AddIcon />
|
||||
<PromptLibrary
|
||||
open={open}
|
||||
onInsertItem={handleOnInsertItem}
|
||||
></PromptLibrary>
|
||||
|
||||
</Button>
|
||||
<Masonry columns={Object.keys(Category).length} spacing={2} sequential>
|
||||
{slottedComposition.map(nugget => {
|
||||
|
||||
})}
|
||||
</Masonry>
|
||||
</div>
|
||||
);
|
||||
}
|
43
src/components/PromptLibrary.test.tsx
Normal file
43
src/components/PromptLibrary.test.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { PromptLibrary } from './PromptLibrary';
|
||||
import { SimpleDialogProps } from './PromptLibrary';
|
||||
import { Category, LibraryItem as LibItemType } from '../lib/prompt';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
const mockOnAddItem = jest.fn();
|
||||
const mockItem: LibItemType = {
|
||||
id: randomUUID(),
|
||||
name: 'Test Item',
|
||||
category: Category.vibes,
|
||||
prompt: 'Test Prompt',
|
||||
};
|
||||
|
||||
const mockOnClose = jest.fn();
|
||||
|
||||
const mockOpen: boolean = true;
|
||||
|
||||
const mockProps: SimpleDialogProps = {
|
||||
open: mockOpen,
|
||||
onInsertItem: mockOnAddItem,
|
||||
};
|
||||
|
||||
test('renders PromptLibrary with add button', () => {
|
||||
render(<PromptLibrary {...mockProps} />);
|
||||
const addButton = screen.getByLabelText('Add');
|
||||
const itemName = screen.getByText((content, element) => {
|
||||
return content.includes(mockItem.name as string);
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(addButton).toBeInTheDocument();
|
||||
// @ts-ignore
|
||||
expect(itemName).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('calls onAddItem when add button is clicked', async () => {
|
||||
render(<PromptLibrary {...mockProps} />);
|
||||
const addButton = screen.getByLabelText('Add');
|
||||
await userEvent.click(addButton);
|
||||
expect(mockOnAddItem).toHaveBeenCalledWith(mockItem);
|
||||
});
|
@ -1,35 +1,66 @@
|
||||
import { Dialog, DialogTitle } from "@material-ui/core";
|
||||
import { Composable } from "./IComposable";
|
||||
import { Library as PromptLibraryType, LibraryItem as LibItemType } from "../lib/prompt";
|
||||
import { Checkbox, Dialog, DialogTitle } from "@material-ui/core";
|
||||
import { LibraryItem as LibItemType, $library, Category } from "../lib/prompt";
|
||||
import { LibraryItem } from "./LibraryItem";
|
||||
import { ChangeEvent } from "react";
|
||||
import { useStore } from "@nanostores/react";
|
||||
|
||||
export interface SimpleDialogProps {
|
||||
open: boolean;
|
||||
onClose: (composable: Composable) => void,
|
||||
library: PromptLibraryType,
|
||||
onAddItem : (item : LibItemType) => void,
|
||||
// onClose: (composable: Composable) => void,
|
||||
// onAddItem: (item: LibItemType) => void,
|
||||
onInsertItem: (item: LibItemType) => void,
|
||||
}
|
||||
|
||||
function title(text: string) {
|
||||
return (!text.length) ? "" : ((text.length === 1) ? text.toUpperCase() : text[0].toUpperCase() + text.substring(1).toLowerCase());
|
||||
}
|
||||
|
||||
|
||||
const hide = ($el: Element) => {
|
||||
if (!$el.classList.contains("hidden")) $el.classList.add("hidden");
|
||||
}
|
||||
|
||||
const show = ($el: Element) => {
|
||||
if (!$el.classList.contains("hidden")) $el.classList.remove("hidden");
|
||||
}
|
||||
|
||||
export function PromptLibrary(props: SimpleDialogProps) {
|
||||
const { onClose, library, open, onAddItem } = props;
|
||||
const { open, onInsertItem } = props;
|
||||
|
||||
const handleClose = () => {
|
||||
// onClose(selectedValue);
|
||||
};
|
||||
const library = useStore($library);
|
||||
|
||||
const handleNuggetClick = (composable : Composable) => {
|
||||
onClose(composable);
|
||||
};
|
||||
const handleOnAddItem = (item: LibItemType) => {
|
||||
// onAddItem(item);
|
||||
}
|
||||
|
||||
const handleOnAddItem = (item : LibItemType) => {
|
||||
const handleOnInsertItem = (item: LibItemType) => {
|
||||
onInsertItem(item);
|
||||
}
|
||||
|
||||
const filterCat = (catKey: string, catVal: string, v: ChangeEvent<HTMLInputElement>) => {
|
||||
const isChecked = v.target.value === '1';
|
||||
document.querySelectorAll(`.category-${catVal}`).forEach($el => {
|
||||
if (isChecked) show($el)
|
||||
else hide($el)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog onClose={handleClose} open={open}>
|
||||
<Dialog open={open}>
|
||||
<DialogTitle>Prompt Library</DialogTitle>
|
||||
<div className="categories">
|
||||
{Object.entries(Category).map(([catKey, catVal]) => {
|
||||
return (
|
||||
<div>
|
||||
<Checkbox onChange={v => filterCat(catKey, catVal, v)} />
|
||||
<span>{title(catVal)}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
library.map(item => <LibraryItem item={item} onAddItem={handleOnAddItem} />)
|
||||
library?.map(item => <LibraryItem item={item} onInsertItem={handleOnInsertItem} />)
|
||||
}
|
||||
</div>
|
||||
</Dialog>
|
||||
|
Reference in New Issue
Block a user