From 20250bce5c3fc154c4e78df6cf9fabe79ff15898 Mon Sep 17 00:00:00 2001 From: Stephen Savage Date: Thu, 6 Mar 2025 15:21:15 +0000 Subject: [PATCH 1/5] added atomic structure --- .../components/{ => atoms/Header}/Header.module.scss | 0 .../components/{ => atoms/Header}/Header.stories.tsx | 2 +- src/ui/components/{ => atoms/Header}/Header.tsx | 4 +++- src/ui/components/atoms/Header/index.ts | 1 + src/ui/components/atoms/index.ts | 1 + src/ui/components/index.ts | 4 +++- src/ui/components/molecules/index.ts | 0 src/ui/components/organisms/index.ts | 0 src/ui/pages/Home.tsx | 10 +++++----- 9 files changed, 14 insertions(+), 8 deletions(-) rename src/ui/components/{ => atoms/Header}/Header.module.scss (100%) rename src/ui/components/{ => atoms/Header}/Header.stories.tsx (93%) rename src/ui/components/{ => atoms/Header}/Header.tsx (76%) create mode 100644 src/ui/components/atoms/Header/index.ts create mode 100644 src/ui/components/atoms/index.ts create mode 100644 src/ui/components/molecules/index.ts create mode 100644 src/ui/components/organisms/index.ts diff --git a/src/ui/components/Header.module.scss b/src/ui/components/atoms/Header/Header.module.scss similarity index 100% rename from src/ui/components/Header.module.scss rename to src/ui/components/atoms/Header/Header.module.scss diff --git a/src/ui/components/Header.stories.tsx b/src/ui/components/atoms/Header/Header.stories.tsx similarity index 93% rename from src/ui/components/Header.stories.tsx rename to src/ui/components/atoms/Header/Header.stories.tsx index b99edfe..34a7fb7 100644 --- a/src/ui/components/Header.stories.tsx +++ b/src/ui/components/atoms/Header/Header.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { Header } from "./Header"; +import Header from "./Header"; const meta: Meta = { title: "Components/Header", diff --git a/src/ui/components/Header.tsx b/src/ui/components/atoms/Header/Header.tsx similarity index 76% rename from src/ui/components/Header.tsx rename to src/ui/components/atoms/Header/Header.tsx index 7a5120d..73726f1 100644 --- a/src/ui/components/Header.tsx +++ b/src/ui/components/atoms/Header/Header.tsx @@ -6,10 +6,12 @@ interface HeaderProps { user: User; } -export const Header: React.FC = ({ user }) => { +const Header: React.FC = ({ user }) => { return (

Hello, {user.name}!

); }; + +export default Header; diff --git a/src/ui/components/atoms/Header/index.ts b/src/ui/components/atoms/Header/index.ts new file mode 100644 index 0000000..d88c989 --- /dev/null +++ b/src/ui/components/atoms/Header/index.ts @@ -0,0 +1 @@ +export { default as Header } from "./Header"; diff --git a/src/ui/components/atoms/index.ts b/src/ui/components/atoms/index.ts new file mode 100644 index 0000000..9e08a64 --- /dev/null +++ b/src/ui/components/atoms/index.ts @@ -0,0 +1 @@ +export * from "./Header"; diff --git a/src/ui/components/index.ts b/src/ui/components/index.ts index eed75a1..61e029e 100644 --- a/src/ui/components/index.ts +++ b/src/ui/components/index.ts @@ -1 +1,3 @@ -export * from "./Header"; \ No newline at end of file +export * from "./atoms"; +// export * from "./molecules"; +// export * from "./organisms"; diff --git a/src/ui/components/molecules/index.ts b/src/ui/components/molecules/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/ui/components/organisms/index.ts b/src/ui/components/organisms/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/ui/pages/Home.tsx b/src/ui/pages/Home.tsx index dc7b7d1..9fd7495 100644 --- a/src/ui/pages/Home.tsx +++ b/src/ui/pages/Home.tsx @@ -1,8 +1,8 @@ "use client"; import React from "react"; -import { Header } from "../components/Header"; -import { USERS } from "../../data"; -import { pickUser } from "../../utils/pickUser"; +import Header from "@/ui/components/atoms/Header/Header"; +import { USERS } from "@/data/users"; +import { pickUser } from "@/utils/pickUser"; export const Home: React.FC = () => { const luckyUser = pickUser(USERS); @@ -10,8 +10,8 @@ export const Home: React.FC = () => { return (
-

Welcome to the Home Page ;)

+

Welcome to the Home Page

This is a simple page component.

); -}; \ No newline at end of file +}; From 6e0d3cf4f28af9673ebe29f749c7c8249db4da79 Mon Sep 17 00:00:00 2001 From: Stephen Savage Date: Thu, 6 Mar 2025 17:43:22 +0000 Subject: [PATCH 2/5] added more complex atom examples --- src/models/image-dto.ts | 27 ++++++ src/models/index.ts | 3 +- src/ui/components/atoms/Cta/Cta.module.scss | 94 +++++++++++++++++++ src/ui/components/atoms/Cta/Cta.stories.tsx | 59 ++++++++++++ src/ui/components/atoms/Cta/Cta.test.tsx | 58 ++++++++++++ src/ui/components/atoms/Cta/Cta.tsx | 77 +++++++++++++++ src/ui/components/atoms/Cta/index.ts | 1 + .../atoms/NextImage/NextImage.module.scss | 13 +++ .../atoms/NextImage/NextImage.test.tsx | 81 ++++++++++++++++ .../components/atoms/NextImage/NextImage.tsx | 59 ++++++++++++ src/ui/components/atoms/NextImage/index.ts | 1 + .../components/atoms/Svg/SvgChevronRight.tsx | 20 ++++ src/ui/components/atoms/Svg/index.ts | 1 + src/ui/components/atoms/index.ts | 3 + tsconfig.json | 61 ++++++------ 15 files changed, 525 insertions(+), 33 deletions(-) create mode 100644 src/models/image-dto.ts create mode 100644 src/ui/components/atoms/Cta/Cta.module.scss create mode 100644 src/ui/components/atoms/Cta/Cta.stories.tsx create mode 100644 src/ui/components/atoms/Cta/Cta.test.tsx create mode 100644 src/ui/components/atoms/Cta/Cta.tsx create mode 100644 src/ui/components/atoms/Cta/index.ts create mode 100644 src/ui/components/atoms/NextImage/NextImage.module.scss create mode 100644 src/ui/components/atoms/NextImage/NextImage.test.tsx create mode 100644 src/ui/components/atoms/NextImage/NextImage.tsx create mode 100644 src/ui/components/atoms/NextImage/index.ts create mode 100644 src/ui/components/atoms/Svg/SvgChevronRight.tsx create mode 100644 src/ui/components/atoms/Svg/index.ts diff --git a/src/models/image-dto.ts b/src/models/image-dto.ts new file mode 100644 index 0000000..3f05bb3 --- /dev/null +++ b/src/models/image-dto.ts @@ -0,0 +1,27 @@ +export interface ImageDTO { + // id: string; + alt: string; + // filename: string; + // mimeType: string; + // filesize: number; + width: number; + height: number; + focalX?: number; + focalY?: number; + sizes?: { + [key: string]: ResizedImage; + }; + // createdAt: string; + // updatedAt: string; + url: string; + // thumbnailURL: string; +} + +export interface ResizedImage { + width: number; + height: number; + mimeType: string; + filesize: number; + filename: string; + url: string; +} diff --git a/src/models/index.ts b/src/models/index.ts index 463548f..e7383f0 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1 +1,2 @@ -export * from "./user"; \ No newline at end of file +export * from "./image-dto"; +export * from "./user"; diff --git a/src/ui/components/atoms/Cta/Cta.module.scss b/src/ui/components/atoms/Cta/Cta.module.scss new file mode 100644 index 0000000..3e8b379 --- /dev/null +++ b/src/ui/components/atoms/Cta/Cta.module.scss @@ -0,0 +1,94 @@ +@use "@/styles/vars.scss" as vars; + +.cta { + cursor: pointer; + display: inline-flex; + justify-content: center; + align-items: center; + padding: 8px 24px; + width: fit-content; + transition: all 0.3s ease; + svg *, + &::after { + transition: all 0.3s ease; + } + &.hidden { + display: none; + visibility: hidden; + } + + &.primary { + border-radius: 30px; + background: transparent; + border: 1.5px solid black; + color: black; + svg * { + stroke: black; + } + &:hover { + background: black; + color: white; + svg * { + stroke: white; + } + } + } + + &.secondary { + border-radius: 30px; + background: transparent; + border: 1.5px solid white; + color: white; + svg * { + stroke: white; + } + &:hover { + background: white; + color: black; + svg * { + stroke: black; + } + } + } + + &.icon { + padding: 0; + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + background: transparent; + border: 1.5px solid black; + svg * { + stroke: black; + } + &:hover { + background: black; + svg * { + stroke: white; + } + } + } + + &.underline { + padding: 4px 0; + position: relative; + &::after { + content: ""; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 1.5px; + background: black; + opacity: 0; + } + &:hover { + &::after { + opacity: 1; + } + } + } +} diff --git a/src/ui/components/atoms/Cta/Cta.stories.tsx b/src/ui/components/atoms/Cta/Cta.stories.tsx new file mode 100644 index 0000000..5735f57 --- /dev/null +++ b/src/ui/components/atoms/Cta/Cta.stories.tsx @@ -0,0 +1,59 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { fn } from "@storybook/test"; +import Cta from "./Cta"; +import SvgChevronRight from "../Svg/SvgChevronRight"; + +// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export +const meta: Meta = { + title: "Atoms/Cta", + component: Cta, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout + layout: "centered", + }, + // More on argTypes: https://storybook.js.org/docs/api/argtypes + // argTypes: { + // backgroundColor: { control: "color" }, + // }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args + args: { onClick: fn() }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Primary: Story = { + args: { + variant: "primary", + children: "CTA", + }, +}; + +export const Secondary: Story = { + args: { + variant: "secondary", + children: "CTA", + }, + globals: { + backgrounds: { value: "dark" }, + }, +}; + +export const Icon: Story = { + args: { + variant: "icon", + }, + render: (args) => ( + + + + ), +}; + +export const Underline: Story = { + args: { + variant: "underline", + children: "CTA", + }, +}; diff --git a/src/ui/components/atoms/Cta/Cta.test.tsx b/src/ui/components/atoms/Cta/Cta.test.tsx new file mode 100644 index 0000000..942914d --- /dev/null +++ b/src/ui/components/atoms/Cta/Cta.test.tsx @@ -0,0 +1,58 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import Cta, { CtaProps } from "./Cta"; + +const dummyData: CtaProps = { + variant: "primary", +}; + +describe(Cta, () => { + it("should render a button element if no href is specified", () => { + const { getByRole } = render(); + const cta = getByRole("button"); + expect(cta).toBeInTheDocument(); + }); + + it("should render a Link element if a href is specified", () => { + const { getByRole } = render( + + ); + const cta = getByRole("link"); + expect(cta).toBeInTheDocument(); + expect(cta).toHaveAttribute("href", "https://www.google.com"); + }); + + it("should render the correct children text", () => { + render(Submit); + render( + + Submit + + ); + const button = screen.getByRole("button"); + const link = screen.getByRole("link"); + // Check if the button contains the correct text + expect(button).toHaveTextContent("Submit"); + expect(link).toHaveTextContent("Submit"); + }); + + it("should call the onClick callback when clicked", () => { + const handleClick = jest.fn(); // Mock function + render(); + render(); + const button = screen.getByRole("button"); + const link = screen.getByRole("link"); + fireEvent.click(button); + fireEvent.click(link); + // Expect the mock function to have been called twice + expect(handleClick).toHaveBeenCalledTimes(2); + }); + + // it("should not display if isHidden property is set to true", () => { + // render(); + // render(); + // const button = screen.queryByRole("button"); + // const link = screen.queryByRole("link"); + // expect(button).toHaveClass("hidden"); + // expect(link).toHaveClass("hidden"); + // }); +}); diff --git a/src/ui/components/atoms/Cta/Cta.tsx b/src/ui/components/atoms/Cta/Cta.tsx new file mode 100644 index 0000000..85b8b3b --- /dev/null +++ b/src/ui/components/atoms/Cta/Cta.tsx @@ -0,0 +1,77 @@ +"use client"; + +import classNames from "classnames"; +import styles from "./Cta.module.scss"; +import Link from "next/link"; +import { FC, PropsWithChildren } from "react"; + +export interface CtaProps extends PropsWithChildren { + className?: string; + variant: "primary" | "secondary" | "icon" | "underline"; + type?: "button" | "submit" | "reset"; + ariaLabel?: string; + href?: string; + newTab?: boolean; + download?: boolean; + onClick?: () => void; + isHidden?: boolean; +} + +const Cta: FC = ({ + className = "", + variant, + type, + ariaLabel, + href, + newTab = false, + download, + onClick = () => { + return; + }, + isHidden, + children, +}) => { + const classNamesString = classNames( + className, + styles.cta, + styles[`${variant}`], + { [styles.hidden]: isHidden } + ); + + return ( + <> + {href ? ( + { + onClick(); + }} + target={newTab ? "_blank" : undefined} + rel={newTab ? "noopener noreferrer" : undefined} + download={download} + > + {children} + + ) : ( + + )} + + ); +}; + +export default Cta; diff --git a/src/ui/components/atoms/Cta/index.ts b/src/ui/components/atoms/Cta/index.ts new file mode 100644 index 0000000..d445e38 --- /dev/null +++ b/src/ui/components/atoms/Cta/index.ts @@ -0,0 +1 @@ +export { default as Cta } from "./Cta"; diff --git a/src/ui/components/atoms/NextImage/NextImage.module.scss b/src/ui/components/atoms/NextImage/NextImage.module.scss new file mode 100644 index 0000000..1d7be9d --- /dev/null +++ b/src/ui/components/atoms/NextImage/NextImage.module.scss @@ -0,0 +1,13 @@ +.imageWrapper { + position: relative; + width: inherit; + height: inherit; + display: flex; + justify-content: center; + align-items: center; +} + +.noFill { + width: fit-content; + height: fit-content; +} diff --git a/src/ui/components/atoms/NextImage/NextImage.test.tsx b/src/ui/components/atoms/NextImage/NextImage.test.tsx new file mode 100644 index 0000000..bfc35f5 --- /dev/null +++ b/src/ui/components/atoms/NextImage/NextImage.test.tsx @@ -0,0 +1,81 @@ +import { render } from "@testing-library/react"; +import NextImage, { NextImageProps } from "./NextImage"; + +describe("NextImage", () => { + it("should render an image ", () => { + const dummyData: NextImageProps = { + image: { + url: "/wiser.svg", + alt: "wiser logo", + height: 100, + width: 100, + }, + }; + const { getByRole } = render(); + const imageElement = getByRole("img"); + expect(imageElement).toBeInTheDocument(); + expect(imageElement).toHaveAttribute("src", dummyData.image.url); + expect(imageElement).toHaveAttribute("alt", dummyData.image.alt); + }); + + it("should render an image with original image width and image height if width, height and fill are not specified", () => { + const dummyData: NextImageProps = { + image: { + url: "/wiser.svg", + alt: "wiser logo", + height: 100, + width: 100, + }, + }; + const { getByRole } = render(); + const imageElement = getByRole("img"); + expect(imageElement).toBeInTheDocument(); + expect(imageElement).toHaveAttribute("width", `${dummyData.image.width}`); + expect(imageElement).toHaveAttribute("height", `${dummyData.image.height}`); + }); + + it("should render an image with specified width and height if they are passed in as attributes", () => { + const dummyData: NextImageProps = { + image: { + url: "/wiser.svg", + alt: "wiser logo", + height: 100, + width: 100, + }, + width: 25, + height: 50, + }; + const { getByRole } = render(); + const imageElement = getByRole("img"); + expect(imageElement).toBeInTheDocument(); + expect(imageElement).toHaveAttribute("width", `${dummyData.width}`); + expect(imageElement).toHaveAttribute("height", `${dummyData.height}`); + }); + + it("should render an image with width and height of parent wrapper when 'fill' is specified", () => { + const dummyData: NextImageProps = { + image: { + url: "/wiser.svg", + alt: "wiser logo", + height: 100, + width: 100, + }, + fill: true, + }; + const { getByRole } = render( +
+ +
+ ); + const imageElement = getByRole("img"); + expect(imageElement).toBeInTheDocument(); + expect(getComputedStyle(imageElement).width).toBe("100%"); + expect(getComputedStyle(imageElement).height).toBe("100%"); + expect( + getComputedStyle(imageElement.parentElement!.parentElement!).width + ).toBe("25px"); + expect( + getComputedStyle(imageElement.parentElement!.parentElement!).height + ).toBe("50px"); + }); +}); diff --git a/src/ui/components/atoms/NextImage/NextImage.tsx b/src/ui/components/atoms/NextImage/NextImage.tsx new file mode 100644 index 0000000..e420e66 --- /dev/null +++ b/src/ui/components/atoms/NextImage/NextImage.tsx @@ -0,0 +1,59 @@ +import styles from "./NextImage.module.scss"; +import { ImageDTO } from "@/models/image-dto"; +import classNames from "classnames"; +import Image from "next/image"; +import { CSSProperties, FC } from "react"; + +export interface NextImageProps { + className?: string; + image: ImageDTO; + width?: number; + height?: number; + fill?: boolean; + objectFit?: "fill" | "contain" | "cover" | "none" | "scale-down"; + objectPosition?: string; + style?: CSSProperties; + priority?: boolean; +} + +const NextImage: FC = ({ + className = "", + image, + width, + height, + fill, + objectFit, + objectPosition, + style = {}, + priority, +}) => { + const fx = image.focalX ? image.focalX : 50; + const fy = image.focalY ? image.focalY : 50; + + return ( +
+ {image.alt} { + // ScrollTrigger.refresh(); + // }} + /> +
+ ); +}; + +export default NextImage; diff --git a/src/ui/components/atoms/NextImage/index.ts b/src/ui/components/atoms/NextImage/index.ts new file mode 100644 index 0000000..7f64730 --- /dev/null +++ b/src/ui/components/atoms/NextImage/index.ts @@ -0,0 +1 @@ +export { default as NextImage } from "./NextImage"; diff --git a/src/ui/components/atoms/Svg/SvgChevronRight.tsx b/src/ui/components/atoms/Svg/SvgChevronRight.tsx new file mode 100644 index 0000000..7a47160 --- /dev/null +++ b/src/ui/components/atoms/Svg/SvgChevronRight.tsx @@ -0,0 +1,20 @@ +const SvgChevronRight = () => { + return ( + + + + ); +}; + +export default SvgChevronRight; diff --git a/src/ui/components/atoms/Svg/index.ts b/src/ui/components/atoms/Svg/index.ts new file mode 100644 index 0000000..efdcf1e --- /dev/null +++ b/src/ui/components/atoms/Svg/index.ts @@ -0,0 +1 @@ +export { default as SvgChevronRight } from "./SvgChevronRight"; diff --git a/src/ui/components/atoms/index.ts b/src/ui/components/atoms/index.ts index 9e08a64..911a2c0 100644 --- a/src/ui/components/atoms/index.ts +++ b/src/ui/components/atoms/index.ts @@ -1 +1,4 @@ +export * from "./Cta"; export * from "./Header"; +export * from "./NextImage"; +export * from "./Svg"; diff --git a/tsconfig.json b/tsconfig.json index 3839bb3..e341801 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,51 +2,52 @@ "compilerOptions": { /* Basic Options */ "jsx": "react-jsx", - "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "esnext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */, + "module": "esnext" /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, "lib": [ "es6", "dom" - ], /* Specify library files to be included in the compilation: */ + ] /* Specify library files to be included in the compilation: */, // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + "sourceMap": true /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./lib", /* Redirect output structure to the directory. */ + "outDir": "./lib" /* Redirect output structure to the directory. */, // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - "removeComments": true, /* Do not emit comments to output. */ + "removeComments": true /* Do not emit comments to output. */, // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */, // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + "strict": true /* Enable all strict type-checking options. */, + "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + "strictNullChecks": true /* Enable strict null checks. */, + "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, + "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ "paths": { "@/*": ["./src/*"] - }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - "typeRoots": [ - "./node_modules/@types" - ], /* List of folders to include type definitions from. */ + // "typeRoots": [ + // "./node_modules/@types" + // ] /* List of folders to include type definitions from. */, "types": [ "node", - "jest" - ], /* Type declaration files to be included in compilation. */ - "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "jest", + "@testing-library/jest-dom" + ] /* Type declaration files to be included in compilation. */, + "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ @@ -54,13 +55,9 @@ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ }, - "include": [ - "./src/**/*" - ], - "exclude": [ - "./node_modules" - ] + "include": ["./src/**/*"], + "exclude": ["./node_modules"] } From 4c4189eb1be442bd14ae84af5b99aaab49aabbec Mon Sep 17 00:00:00 2001 From: Stephen Savage Date: Thu, 6 Mar 2025 17:51:06 +0000 Subject: [PATCH 3/5] added Downloader component --- src/ui/components/index.ts | 2 +- .../Downloader/Downloader.stories.tsx | 18 ++++++++++++++++++ .../organisms/Downloader/Downloader.tsx | 18 ++++++++++++++++++ .../components/organisms/Downloader/index.ts | 1 + src/ui/components/organisms/index.ts | 1 + 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/ui/components/organisms/Downloader/Downloader.stories.tsx create mode 100644 src/ui/components/organisms/Downloader/Downloader.tsx create mode 100644 src/ui/components/organisms/Downloader/index.ts diff --git a/src/ui/components/index.ts b/src/ui/components/index.ts index 61e029e..3ce38df 100644 --- a/src/ui/components/index.ts +++ b/src/ui/components/index.ts @@ -1,3 +1,3 @@ export * from "./atoms"; // export * from "./molecules"; -// export * from "./organisms"; +export * from "./organisms"; diff --git a/src/ui/components/organisms/Downloader/Downloader.stories.tsx b/src/ui/components/organisms/Downloader/Downloader.stories.tsx new file mode 100644 index 0000000..9e164bc --- /dev/null +++ b/src/ui/components/organisms/Downloader/Downloader.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import Downloader from "./Downloader"; + +// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export +const meta: Meta = { + title: "Organisms/Downloader", + component: Downloader, + parameters: { + layout: "fullscreen", + }, +}; + +export default meta; +type Story = StoryObj; + +export const Primary: Story = { + args: {}, +}; diff --git a/src/ui/components/organisms/Downloader/Downloader.tsx b/src/ui/components/organisms/Downloader/Downloader.tsx new file mode 100644 index 0000000..9bac8c5 --- /dev/null +++ b/src/ui/components/organisms/Downloader/Downloader.tsx @@ -0,0 +1,18 @@ +import Cta from "@/ui/components/atoms/Cta/Cta"; + +const Downloader = () => { + return ( +
+

Download

+
    +
  • + + Top Secret + +
  • +
+
+ ); +}; + +export default Downloader; diff --git a/src/ui/components/organisms/Downloader/index.ts b/src/ui/components/organisms/Downloader/index.ts new file mode 100644 index 0000000..c34e168 --- /dev/null +++ b/src/ui/components/organisms/Downloader/index.ts @@ -0,0 +1 @@ +export { default as Downloader } from "./Downloader"; diff --git a/src/ui/components/organisms/index.ts b/src/ui/components/organisms/index.ts index e69de29..4f4a272 100644 --- a/src/ui/components/organisms/index.ts +++ b/src/ui/components/organisms/index.ts @@ -0,0 +1 @@ +export * from "./Downloader"; From 01cc303ca737d08045e2db528fc5505bfb2d05dd Mon Sep 17 00:00:00 2001 From: Kieren Coetzee Date: Fri, 7 Mar 2025 09:20:43 +0000 Subject: [PATCH 4/5] add missing dependencies: classnames, identity-obj-proxy --- package-lock.json | 28 ++++++++++++++++++++++++++++ package.json | 12 +++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f61e3e7..951d594 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "classnames": "^2.5.1", "ini": "^5.0.0", "next": "^15.0.0", "react": "19.0.0", @@ -46,6 +47,7 @@ "css-loader": "^7.1.2", "eslint": "^8.36.0", "eslint-plugin-storybook": "^0.11.3", + "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.9.2", @@ -6726,6 +6728,12 @@ "node": ">=0.10.0" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -9226,6 +9234,13 @@ "dev": true, "license": "MIT" }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "dev": true, + "license": "(Apache-2.0 OR MPL-1.1)" + }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -9604,6 +9619,19 @@ "postcss": "^8.1.0" } }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dev": true, + "license": "MIT", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ieee754": { "version": "1.2.1", "dev": true, diff --git a/package.json b/package.json index c07a64d..4aba60e 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "classnames": "^2.5.1", "ini": "^5.0.0", "next": "^15.0.0", "react": "19.0.0", @@ -73,9 +74,6 @@ "tsnode-di": "0.0.3" }, "devDependencies": { - "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", "@chromatic-com/storybook": "^3.2.4", "@storybook/addon-essentials": "^8.5.8", "@storybook/addon-interactions": "^8.5.8", @@ -83,6 +81,9 @@ "@storybook/blocks": "^8.5.8", "@storybook/nextjs": "^8.5.8", "@storybook/test": "^8.5.8", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", "@types/chai": "^4.3.4", "@types/cors": "^2.8.13", "@types/jest": "^29.5.14", @@ -96,9 +97,10 @@ "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", "eslint": "^8.36.0", + "eslint-plugin-storybook": "^0.11.3", + "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "eslint-plugin-storybook": "^0.11.3", "mini-css-extract-plugin": "^2.9.2", "mocha": "^10.2.0", "nodemon": "^1.12.1", @@ -106,8 +108,8 @@ "nyc": "^15.1.0", "sass": "^1.85.0", "sass-loader": "^16.0.5", - "ts-jest": "^29.2.6", "storybook": "^8.5.8", + "ts-jest": "^29.2.6", "ts-loader": "^9.4.2", "ts-node": "^10.9.2", "typedoc": "^0.23.28", From aebe663fc3bb6730e5dcbbf2db5d1ae3577b4c39 Mon Sep 17 00:00:00 2001 From: John Fedoruk Date: Mon, 17 Mar 2025 13:15:24 -0500 Subject: [PATCH 5/5] Linting --- src/hooks/useToggle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useToggle.ts b/src/hooks/useToggle.ts index 492e600..f08cdc9 100644 --- a/src/hooks/useToggle.ts +++ b/src/hooks/useToggle.ts @@ -1,7 +1,7 @@ "use client"; import { useState, useCallback } from "react"; -export function useToggle(initial_value: boolean = false): [boolean, () => void] { +export function useToggle(initial_value = false): [boolean, () => void] { const [value, setValue] = useState(initial_value); const toggle = useCallback(() => { setValue((prev) => !prev);