Skip to content

Commit

Permalink
feat(samples preview): toggle preview modes
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexGarrixen committed Jan 15, 2024
1 parent a8495bd commit 3e75a18
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 15 deletions.
30 changes: 30 additions & 0 deletions src/components/samples-preview/__tests__/toggle-button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, it } from "@jest/globals";
import { fireEvent, render } from "@testing-library/react";

import * as ToggleButton from "../toggle-button";

function Render() {
return (
<ToggleButton.Root defaultValue="one">
<ToggleButton.Item value="one">one</ToggleButton.Item>
<ToggleButton.Item value="two">two</ToggleButton.Item>
</ToggleButton.Root>
);
}

describe("Samples Preview / ToggleButton", () => {
it("Correct rendering and unmount", () => {
const screen = render(<Render />);

expect(() => screen.unmount()).not.toThrow();
});

it("Should change active item", () => {
const screen = render(<Render />);
const btnTwo = screen.getByText("two");

fireEvent.click(btnTwo);

expect(btnTwo.getAttribute("aria-pressed")).toBe("true");
});
});
11 changes: 0 additions & 11 deletions src/components/samples-preview/samples-grid.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
"use client";

import Masonry, { ResponsiveMasonry } from "react-responsive-masonry";
import { useAtomValue } from "jotai";
import Color from "color";
import { css } from "@root/styled-system/css";

import { background, foreground } from "@/store";

import { fgVar, bgVar } from "./utils";
import { Sample1 } from "./sample-1";
import { Sample2 } from "./sample-2";
import { Sample3 } from "./sample-3";
Expand All @@ -22,16 +17,10 @@ import { Sample12 } from "./sample-12";
import { Sample13 } from "./sample-13";

export function SamplesGrid() {
const bg = useAtomValue(background);
const fg = useAtomValue(foreground);
const bgRgb = Color(bg).rgb().round().array();
const fgRgb = Color(fg).rgb().round().array();

return (
<ResponsiveMasonry
className={css({ "& article": { border: "1px solid", borderColor: "border-secondary" } })}
columnsCountBreakPoints={{ 0: 1, 640: 2, 1024: 3 }}
style={{ [fgVar as string]: fgRgb.join(" "), [bgVar as string]: bgRgb.join(" ") }}
>
<Masonry gutter="20px">
<Sample1 />
Expand Down
22 changes: 18 additions & 4 deletions src/components/samples-preview/samples-preview.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
"use client";

import { useState } from "react";
import { useAtomValue } from "jotai";
import Color from "color";
import { css } from "@root/styled-system/css";
import Color from "color";

import { background, foreground } from "@/store";

import * as ToggleButton from "./toggle-button";
import { MinimalistPage } from "./minimalist-page";
import { SamplesGrid } from "./samples-grid";
import { fgVar, bgVar } from "./utils";

const classes = {
root: css({ my: "6" }),
text: css({ textStyle: "text-lg", textAlign: "center", mb: "3", color: "text-secondary" }),
toggle: css({ textAlign: "center", mb: "6" }),
};

const previewModes = {
minimalistPage: "minimalist-page",
uiElements: "ui-elements",
};

export function SamplesPreview() {
const bg = useAtomValue(background);
const fg = useAtomValue(foreground);
const [previewMode, setPreviewMode] = useState<string>(previewModes.minimalistPage);
const bgRgb = Color(bg).rgb().round().array().join(" ");
const fgRgb = Color(fg).rgb().round().array().join(" ");

Expand All @@ -25,8 +34,13 @@ export function SamplesPreview() {
className={classes.root}
style={{ [fgVar as string]: fgRgb, [bgVar as string]: bgRgb }}
>
<p className={classes.text}>Preview</p>
<MinimalistPage />
<div className={classes.toggle}>
<ToggleButton.Root defaultValue={previewMode} onChange={setPreviewMode}>
<ToggleButton.Item value={previewModes.minimalistPage}>Minimalist Page</ToggleButton.Item>
<ToggleButton.Item value={previewModes.uiElements}>UI Elements</ToggleButton.Item>
</ToggleButton.Root>
</div>
{previewMode === previewModes.minimalistPage ? <MinimalistPage /> : <SamplesGrid />}
</section>
);
}
73 changes: 73 additions & 0 deletions src/components/samples-preview/toggle-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { Dispatch, SetStateAction, ReactNode } from "react";

import { createContext, useState, useContext } from "react";
import { css, cx } from "@root/styled-system/css";

import { Button } from "@/components/primitives/button";

const classes = {
root: css({
p: "1",
bgColor: "bg-primary",
display: "inline-block",
rounded: "xl",
border: "1px solid",
borderColor: "border-secondary",
}),

item: css({ border: "none" }),

enabledItem: css({ bgColor: "bg-tertiary", color: "text-primary" }),

disabledItem: css({ color: "text-secondary" }),
};

interface ContextValue {
value: string;
setValue: Dispatch<SetStateAction<string>>;
onChange?: (value: string) => void;
}

const Context = createContext<Pick<ContextValue, "value">>({
value: "",
});

interface RootProps extends Pick<ContextValue, "onChange"> {
children: ReactNode;
defaultValue: string;
}

export function Root({ children, defaultValue, onChange }: RootProps) {
const [value, setValue] = useState(defaultValue);
const valueProvider: ContextValue = { value, setValue, onChange };

return (
<Context.Provider value={valueProvider}>
<div className={classes.root}>{children}</div>
</Context.Provider>
);
}

interface ItemProps extends Pick<ContextValue, "value"> {
children: ReactNode;
}

export function Item({ children, value }: ItemProps) {
const { value: activeValue, setValue, onChange } = useContext(Context) as ContextValue;
const isEnabled = value === activeValue;

function onClick() {
setValue(value);
onChange?.(value);
}

return (
<Button
aria-pressed={isEnabled ? "true" : "false"}
className={cx(classes.item, isEnabled ? classes.enabledItem : classes.disabledItem)}
onClick={onClick}
>
{children}
</Button>
);
}

0 comments on commit 3e75a18

Please sign in to comment.