├── .babelrc.json ├── .gitignore ├── .storybook ├── main.ts └── preview.ts ├── jest.config.js ├── license ├── package-lock.json ├── package.json ├── readme.md ├── src ├── components │ ├── Button │ │ ├── Button.stories.tsx │ │ ├── Button.test.tsx │ │ ├── Button.tsx │ │ ├── Button.types.ts │ │ └── index.ts │ ├── Input │ │ ├── Input.stories.tsx │ │ ├── Input.test.tsx │ │ ├── Input.tsx │ │ ├── Input.types.ts │ │ └── index.ts │ ├── index.ts │ └── test.tsx └── index.ts ├── tsconfig.json └── tsup.config.ts /.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceType": "unambiguous", 3 | "presets": [ 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "targets": { 8 | "chrome": 100 9 | } 10 | } 11 | ], 12 | "@babel/preset-typescript", 13 | "@babel/preset-react" 14 | ], 15 | "plugins": [] 16 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | yarn.lock 4 | dist 5 | pnpm-lock.yaml -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook/react-webpack5"; 2 | const config: StorybookConfig = { 3 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 4 | addons: [ 5 | "@storybook/addon-links", 6 | "@storybook/addon-essentials", 7 | "@storybook/addon-interactions", 8 | ], 9 | framework: { 10 | name: "@storybook/react-webpack5", 11 | options: {}, 12 | }, 13 | docs: { 14 | autodocs: "tag", 15 | }, 16 | }; 17 | export default config; 18 | -------------------------------------------------------------------------------- /.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from "@storybook/react"; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | actions: { argTypesRegex: "^on[A-Z].*" }, 6 | controls: { 7 | matchers: { 8 | color: /(background|color)$/i, 9 | date: /Date$/, 10 | }, 11 | }, 12 | }, 13 | }; 14 | 15 | export default preview; 16 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | module.exports = { 7 | // All imported modules in your tests should be mocked automatically 8 | // automock: false, 9 | 10 | // Stop running tests after `n` failures 11 | // bail: 0, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "C:\\Users\\Lenovo\\AppData\\Local\\Temp\\jest", 15 | 16 | // Automatically clear mock calls, instances, contexts and results before every test 17 | // clearMocks: false, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | // collectCoverage: false, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: undefined, 24 | 25 | // The directory where Jest should output its coverage files 26 | // coverageDirectory: undefined, 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | // coveragePathIgnorePatterns: [ 30 | // "\\\\node_modules\\\\" 31 | // ], 32 | 33 | // Indicates which provider should be used to instrument code for coverage 34 | // coverageProvider: "babel", 35 | 36 | // A list of reporter names that Jest uses when writing coverage reports 37 | // coverageReporters: [ 38 | // "json", 39 | // "text", 40 | // "lcov", 41 | // "clover" 42 | // ], 43 | 44 | // An object that configures minimum threshold enforcement for coverage results 45 | // coverageThreshold: undefined, 46 | 47 | // A path to a custom dependency extractor 48 | // dependencyExtractor: undefined, 49 | 50 | // Make calling deprecated APIs throw helpful error messages 51 | // errorOnDeprecated: false, 52 | 53 | // The default configuration for fake timers 54 | // fakeTimers: { 55 | // "enableGlobally": false 56 | // }, 57 | 58 | // Force coverage collection from ignored files using an array of glob patterns 59 | // forceCoverageMatch: [], 60 | 61 | // A path to a module which exports an async function that is triggered once before all test suites 62 | // globalSetup: undefined, 63 | 64 | // A path to a module which exports an async function that is triggered once after all test suites 65 | // globalTeardown: undefined, 66 | 67 | // A set of global variables that need to be available in all test environments 68 | // globals: {}, 69 | 70 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 71 | // maxWorkers: "50%", 72 | 73 | // An array of directory names to be searched recursively up from the requiring module's location 74 | // moduleDirectories: [ 75 | // "node_modules" 76 | // ], 77 | 78 | // An array of file extensions your modules use 79 | // moduleFileExtensions: [ 80 | // "js", 81 | // "mjs", 82 | // "cjs", 83 | // "jsx", 84 | // "ts", 85 | // "tsx", 86 | // "json", 87 | // "node" 88 | // ], 89 | 90 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 91 | // moduleNameMapper: {}, 92 | 93 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 94 | // modulePathIgnorePatterns: [], 95 | 96 | // Activates notifications for test results 97 | // notify: false, 98 | 99 | // An enum that specifies notification mode. Requires { notify: true } 100 | // notifyMode: "failure-change", 101 | 102 | // A preset that is used as a base for Jest's configuration 103 | // preset: undefined, 104 | 105 | // Run tests from one or more projects 106 | // projects: undefined, 107 | 108 | // Use this configuration option to add custom reporters to Jest 109 | // reporters: undefined, 110 | 111 | // Automatically reset mock state before every test 112 | // resetMocks: false, 113 | 114 | // Reset the module registry before running each individual test 115 | // resetModules: false, 116 | 117 | // A path to a custom resolver 118 | // resolver: undefined, 119 | 120 | // Automatically restore mock state and implementation before every test 121 | // restoreMocks: false, 122 | 123 | // The root directory that Jest should scan for tests and modules within 124 | // rootDir: undefined, 125 | 126 | // A list of paths to directories that Jest should use to search for files in 127 | // roots: [ 128 | // "" 129 | // ], 130 | 131 | // Allows you to use a custom runner instead of Jest's default test runner 132 | // runner: "jest-runner", 133 | 134 | // The paths to modules that run some code to configure or set up the testing environment before each test 135 | // setupFiles: [], 136 | 137 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 138 | // setupFilesAfterEnv: [], 139 | 140 | // The number of seconds after which a test is considered as slow and reported as such in the results. 141 | // slowTestThreshold: 5, 142 | 143 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 144 | // snapshotSerializers: [], 145 | 146 | // The test environment that will be used for testing 147 | testEnvironment: "jsdom", 148 | 149 | // Options that will be passed to the testEnvironment 150 | // testEnvironmentOptions: {}, 151 | 152 | // Adds a location field to test results 153 | // testLocationInResults: false, 154 | 155 | // The glob patterns Jest uses to detect test files 156 | // testMatch: [ 157 | // "**/__tests__/**/*.[jt]s?(x)", 158 | // "**/?(*.)+(spec|test).[tj]s?(x)" 159 | // ], 160 | 161 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 162 | // testPathIgnorePatterns: [ 163 | // "\\\\node_modules\\\\" 164 | // ], 165 | 166 | // The regexp pattern or array of patterns that Jest uses to detect test files 167 | // testRegex: [], 168 | 169 | // This option allows the use of a custom results processor 170 | // testResultsProcessor: undefined, 171 | 172 | // This option allows use of a custom test runner 173 | // testRunner: "jest-circus/runner", 174 | 175 | // A map from regular expressions to paths to transformers 176 | // transform: undefined, 177 | 178 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 179 | // transformIgnorePatterns: [ 180 | // "\\\\node_modules\\\\", 181 | // "\\.pnp\\.[^\\\\]+$" 182 | // ], 183 | 184 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 185 | // unmockedModulePathPatterns: undefined, 186 | 187 | // Indicates whether each individual test should be reported during the run 188 | // verbose: undefined, 189 | 190 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 191 | // watchPathIgnorePatterns: [], 192 | 193 | // Whether to use watchman for file crawling 194 | // watchman: true, 195 | }; 196 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Pelumi Eyitimonwa Akintokun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@timonwa/demo-ui-library", 3 | "type": "module", 4 | "version": "1.0.3", 5 | "description": "A demo on how to create a React UI component library using React, Typescript, Rollup, Storybook, Jest and React testing library.", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/Timonwa/demo-ui-library.git" 9 | }, 10 | "main": "./dist/esm/index.js", 11 | "module": "./dist/esm/index.js", 12 | "types": "./dist/esm/index.d.ts", 13 | "scripts": { 14 | "test": "jest", 15 | "build": "tsup", 16 | "build:dev": "tsup --watch", 17 | "storybook": "storybook dev -p 6006", 18 | "build-storybook": "storybook build" 19 | }, 20 | "keywords": [ 21 | "demo", 22 | "ui", 23 | "react", 24 | "library", 25 | "tutorial", 26 | "learning", 27 | "educational", 28 | "example", 29 | "beginner-friendly", 30 | "practice" 31 | ], 32 | "files": [ 33 | "dist" 34 | ], 35 | "sideEffects": false, 36 | "exports": { 37 | ".": { 38 | "import": "./dist/esm/index.js", 39 | "require": "./dist/cjs/index.js" 40 | } 41 | }, 42 | "author": "Timonwa Akintokun", 43 | "license": "MIT", 44 | "devDependencies": { 45 | "@babel/preset-env": "^7.21.5", 46 | "@babel/preset-react": "^7.18.6", 47 | "@babel/preset-typescript": "^7.21.5", 48 | "@storybook/addon-essentials": "7.0.8", 49 | "@storybook/addon-interactions": "7.0.8", 50 | "@storybook/addon-links": "7.0.8", 51 | "@storybook/blocks": "7.0.8", 52 | "@storybook/react": "7.0.8", 53 | "@storybook/react-webpack5": "7.0.8", 54 | "@storybook/testing-library": "0.0.14-next.2", 55 | "@testing-library/jest-dom": "^5.16.5", 56 | "@testing-library/react": "^14.0.0", 57 | "@testing-library/user-event": "^14.4.3", 58 | "@types/jest": "^29.5.1", 59 | "@types/react": "^18.2.2", 60 | "@types/styled-components": "^5.1.26", 61 | "@zayne-labs/tsconfig": "^0.1.1", 62 | "jest": "^29.5.0", 63 | "jest-environment-jsdom": "^29.5.0", 64 | "storybook": "7.0.8", 65 | "terser": "^5.31.6", 66 | "tslib": "^2.7.0", 67 | "tsup": "^8.2.4", 68 | "typescript": "^5.5.4" 69 | }, 70 | "peerDependencies": { 71 | "react": "^18.2.0", 72 | "react-dom": "18.2.0", 73 | "styled-components": "^5.3.10" 74 | }, 75 | "dependencies": { 76 | "@timonwa/demo-ui-library": "link:" 77 | } 78 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # My Demo UI Library 2 | 3 | This is a demo UI component library created using React, TypeScript, Rollup, Storybook, Jest, and React Testing Library. It was created for an article update for [LogRocket](https://blog.logrocket.com/author/pelumiakintokun/) on how to create a UI component library in React. You can read the article [here](https://blog.logrocket.com/build-component-library-react-typescript/). 4 | 5 | Visit my blog, [Timonwa's Notes](https://blog.timonwa.com), for awesome technical contents like articles, code snippets, tech goodies, community projects and more. 6 | 7 | ## Installation 8 | 9 | You can install this demo UI library using npm: 10 | 11 | ``` 12 | npm install @timonwa/demo-ui-library 13 | ``` 14 | 15 | ## Usage 16 | 17 | To use this demo UI library in your project, import the components you need from the library and use them in your React components. 18 | 19 | ```jsx 20 | import React from "react"; 21 | import { Input, Button } from "@timonwa/demo-ui-library"; 22 | 23 | function App() { 24 | return ( 25 |
26 | console.log(e.target.value)} 34 | placeholder="Enter your name here" 35 | /> 36 |
44 | ); 45 | } 46 | 47 | export default App; 48 | ``` 49 | 50 | ## Contributing 51 | 52 | This library is a demo for learning purposes only and is not intended to be contributed to. You can fork the repository and use the code for your personal use or learning. 53 | 54 | ### Steps 55 | 56 | - Fork the repository. 57 | - Clone the repository to your local machine. 58 | - Install the dependencies using `npm install`. 59 | - View the components in the browser using `npm run storybook`. 60 | - Make your changes. 61 | - Test the changes using `npm test`. 62 | - Build the library using `npm run build`. 63 | - Commit the changes and push them to your forked repository. 64 | - Publish the package on [npm](https://www.npmjs.com/). 65 | - Install and use the package in your project. 66 | 67 | ## License 68 | 69 | This demo UI library is licensed under the [MIT License](https://github.com/Timonwa/demo-ui-library/blob/main/license). 70 | 71 | ## Connect 72 | 73 | More of my articles can be found in [Timonwa's Notes](https://blog.timonwa.com). Connect with me on Twitter: [@timonwa\_](https://twitter.com/timonwa_) 74 | -------------------------------------------------------------------------------- /src/components/Button/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Meta, StoryObj } from "@storybook/react"; 3 | import Button from "./Button"; 4 | 5 | const meta: Meta = { 6 | component: Button, 7 | title: "Marbella/Button", 8 | argTypes: {}, 9 | }; 10 | export default meta; 11 | 12 | type Story = StoryObj; 13 | 14 | export const Primary: Story = (args) => ( 15 |