diff --git a/README.md b/README.md index 3defec7d..1ebc4219 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,10 @@ Here's what's coming up for Microbundle: - [mdx-deck-live-code](https://github.com/JReinhold/mdx-deck-live-code) A library for [mdx-deck](https://github.com/jxnblk/mdx-deck) to do live React and JS coding directly in slides. - [react-router-ext](https://github.com/ri7nz/react-router-ext) An Extended [react-router-dom](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom) with simple usage. +## 👀 Examples + +- [react-ui-typescript-microbundle](examples/react-ui-typescript-storybook) A React UI library created with Typescript, styled-components, styled-system & Storybook + ## 🥂 License [MIT](https://oss.ninja/mit/developit/) diff --git a/examples/react-ui-typescript-storybook/.storybook/Decorators.js b/examples/react-ui-typescript-storybook/.storybook/Decorators.js new file mode 100644 index 00000000..4d515a97 --- /dev/null +++ b/examples/react-ui-typescript-storybook/.storybook/Decorators.js @@ -0,0 +1,11 @@ +import React from 'react'; +import { ThemeProvider } from '../src/theme/styled-components'; +import { Box, theme } from '../src' + +export const Theming = (storyFn) => ( + + + {storyFn()} + + +); diff --git a/examples/react-ui-typescript-storybook/.storybook/addons.js b/examples/react-ui-typescript-storybook/.storybook/addons.js new file mode 100644 index 00000000..bdc6cfa3 --- /dev/null +++ b/examples/react-ui-typescript-storybook/.storybook/addons.js @@ -0,0 +1,4 @@ +import '@storybook/addon-actions/register'; +import '@storybook/addon-links/register'; +import '@storybook/addon-storysource/register'; +import '@storybook/addon-knobs/register'; diff --git a/examples/react-ui-typescript-storybook/.storybook/config.js b/examples/react-ui-typescript-storybook/.storybook/config.js new file mode 100644 index 00000000..bb9bb4f7 --- /dev/null +++ b/examples/react-ui-typescript-storybook/.storybook/config.js @@ -0,0 +1,24 @@ +import { addParameters, configure, addDecorator } from '@storybook/react'; +import { withKnobs } from '@storybook/addon-knobs'; +import { themes } from '@storybook/theming'; +import { Theming } from './Decorators' + +// Option defaults. +addParameters({ + options: { + name: 'Theme', + theme: themes.dark, + }, +}); + +// Decorators +addDecorator(Theming); +addDecorator(withKnobs); + +// automatically import all files ending in *.stories.js +const req = require.context('../src', true, /\.stories\.tsx$/); +function loadStories() { + req.keys().forEach(filename => req(filename)); +} + +configure(loadStories, module); diff --git a/examples/react-ui-typescript-storybook/.storybook/webpack.config.js b/examples/react-ui-typescript-storybook/.storybook/webpack.config.js new file mode 100644 index 00000000..bc235950 --- /dev/null +++ b/examples/react-ui-typescript-storybook/.storybook/webpack.config.js @@ -0,0 +1,23 @@ +module.exports = ({ config, mode }) => { + config.module.rules.push({ + test: /\.(ts|tsx)$/, + loader: require.resolve('babel-loader'), + options: { + presets: [['react-app', { flow: false, typescript: true }]], + }, + }); + + config.module.rules.push({ + test: /\.stories\.tsx?$/, + loaders: [ + { + loader: require.resolve('@storybook/addon-storysource/loader'), + options: { parser: 'typescript' }, + }, + ], + enforce: 'pre', + }); + + config.resolve.extensions.push('.ts', '.tsx'); + return config; +}; diff --git a/examples/react-ui-typescript-storybook/README.md b/examples/react-ui-typescript-storybook/README.md new file mode 100644 index 00000000..4237266e --- /dev/null +++ b/examples/react-ui-typescript-storybook/README.md @@ -0,0 +1,44 @@ +# react-ui-typescript-microbundle + +React UI Library created with Typescript & microbundle (storybook included) + +## Features + +- Components Built with React & Typescript +- styled-components +- styled-system +- polished +- Storybook setup for development isolation in Typescript + +--- + +## Setup + +```shell +git clone +cd react-ui-typescript-microbundle +yarn # or npm i +yarn storybook +``` + +## Build + +```shell +yarn build +``` + +## Build storybook + +```shell +yarn build:storybook +``` + +## Example in the wild + +- [coeli-ui-staging](https://www.npmjs.com/package/coeli-ui-staging): testing the library with some basics + +## Questions? + +Please send me any question via [Twitter](https://twitter.com/hhg2288) + +### Thanks! ;) diff --git a/examples/react-ui-typescript-storybook/package.json b/examples/react-ui-typescript-storybook/package.json new file mode 100644 index 00000000..7d7d36c9 --- /dev/null +++ b/examples/react-ui-typescript-storybook/package.json @@ -0,0 +1,58 @@ +{ + "name": "react-ui-typescript-microbundle", + "version": "0.1.3", + "description": "React UI Library created with Typescript & microbundle (storybook included)", + "main": "dist/index.js", + "umd:main": "dist/index.umd.js", + "module": "dist/index.mjs", + "source": "src/index.ts", + "types": "dist/index.d.ts", + "scripts": { + "dev": "microbundle watch --jsx react -f es,cjs", + "build": "microbundle --jsx react -f es,cjs", + "test": "jest", + "storybook": "start-storybook -p 6006", + "build:storybook": "build-storybook" + }, + "keywords": [ + "ui", + "react", + "library", + "design system" + ], + "author": "Horacio Herrera (twitter: @hhg2288)", + "license": "MIT", + "devDependencies": { + "@babel/core": "^7.4.0", + "@storybook/addon-actions": "^5.0.5", + "@storybook/addon-knobs": "^5.0.5", + "@storybook/addon-links": "^5.0.5", + "@storybook/addon-storysource": "^5.0.5", + "@storybook/addons": "^5.0.5", + "@storybook/react": "^5.0.5", + "@types/jest": "^24.0.11", + "@types/storybook__addon-actions": "^3.4.2", + "@types/storybook__addon-links": "^3.3.4", + "@types/storybook__react": "^4.0.1", + "@types/styled-system": "^4.0.0", + "babel-loader": "^8.0.5", + "core-js": "^3.0.0", + "jest": "^24.5.0", + "microbundle": "^0.11.0", + "ts-jest": "^24.0.0" + }, + "peerDependencies": { + "polished": ">=3.1.0", + "react": ">=16", + "react-dom": ">=16", + "react-feather": ">=1.1.6", + "styled-components": ">=4.0.0", + "styled-system": ">=4.0.8" + }, + "dependencies": { + "polished": "^3.1.0", + "react-feather": "^1.1.6", + "styled-components": "^4.2.0", + "styled-system": "^4.0.8" + } +} diff --git a/examples/react-ui-typescript-storybook/src/components/Box/Box.stories.tsx b/examples/react-ui-typescript-storybook/src/components/Box/Box.stories.tsx new file mode 100644 index 00000000..8c694717 --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/components/Box/Box.stories.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { storiesOf } from "@storybook/react"; +import { number } from '@storybook/addon-knobs'; + + +import { Box } from "./Box"; + +storiesOf("Box", module) + .add("Base Box", () => ( + + margin: 2, padding: 2 + + )) + .add("Box with Margin & Padding", () => ( + + margin: 2, padding: 2 + + )); diff --git a/examples/react-ui-typescript-storybook/src/components/Box/Box.tsx b/examples/react-ui-typescript-storybook/src/components/Box/Box.tsx new file mode 100644 index 00000000..483d9850 --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/components/Box/Box.tsx @@ -0,0 +1,42 @@ +import styled from "../../theme/styled-components"; + +import { + color, + ColorProps, + space, + SpaceProps, + width, + WidthProps, + fontSize, + FontSizeProps, + flex, + FlexProps, + order, + OrderProps, + alignSelf, + AlignSelfProps +} from "styled-system"; + +import { css, themed } from "../../theme/utils"; + +export type BoxProps = ColorProps & + SpaceProps & + WidthProps & + FontSizeProps & + FlexProps & + OrderProps & + AlignSelfProps; + +export const Box = styled('div')` + ${space} + ${width} + ${fontSize} + ${color} + ${flex} + ${order} + ${alignSelf} + ${themed("Box")} + ${css} +`; + +Box.displayName = "Box"; diff --git a/examples/react-ui-typescript-storybook/src/components/Button/Button.stories.tsx b/examples/react-ui-typescript-storybook/src/components/Button/Button.stories.tsx new file mode 100644 index 00000000..42dc7921 --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/components/Button/Button.stories.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { storiesOf } from "@storybook/react"; +import { action } from "@storybook/addon-actions"; +import { text, select, boolean } from "@storybook/addon-knobs"; + +import { Button } from "./Button"; + +const knobs = { + variant: { + label: "Variant", + options: ["primary", "secondary"] + }, + icon: { + label: "Icon", + options: [] + }, + disabled: { + label: 'disabled', + defaultValue: false + } +}; + +storiesOf("Button", module) + .add("Primary Button", () => ( + + )) + .add("Secondary Button", () => ( + + )); diff --git a/examples/react-ui-typescript-storybook/src/components/Button/Button.tsx b/examples/react-ui-typescript-storybook/src/components/Button/Button.tsx new file mode 100644 index 00000000..d2e4643a --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/components/Button/Button.tsx @@ -0,0 +1,66 @@ +import * as React from "react"; +import styled from "../../theme/styled-components"; +import { + fontWeight, + borders, + borderColor, + buttonStyle, + borderRadius +} from "styled-system"; +import { themed } from "../../theme/utils"; +import { Flex } from "../Flex/Flex"; +import { COLOR_PRIMARY } from "../../theme/theme"; + + +const BaseButton = styled(Flex)` + appearance: none; + text-align: center; + line-height: inherit; + text-decoration: none; + transition: background-color 0.25s; + white-space: nowrap; + cursor: ${({ disabled }) => (disabled ? "not-allowed" : "pointer")}; + opacity: ${({ disabled }) => (disabled ? 0.2 : 1)}; + transition: opacity 0.2s ease; + + ${fontWeight} + ${borders} + ${borderColor} + ${borderRadius} + ${buttonStyle} + ${themed("Button")} + + > * { + margin: 0 4px; + color: inherit; + } +`; + +export interface ButtonProps { + disabled?: boolean; + onClick: () => void; + variant?: "primary" | "secondary"; +} + +export const Button: React.FunctionComponent = ({ + children, + disabled = false, + onClick, + variant = "primary", + ...rest +}) => ( + + {children} + +); diff --git a/examples/react-ui-typescript-storybook/src/components/Flex/Flex.stories.tsx b/examples/react-ui-typescript-storybook/src/components/Flex/Flex.stories.tsx new file mode 100644 index 00000000..4ce500b6 --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/components/Flex/Flex.stories.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { storiesOf } from "@storybook/react"; +import { number } from '@storybook/addon-knobs'; + + +import { Flex } from "./Flex"; + +storiesOf("Flex", module) +.add("Base Flex", () => ( + + Base Flex + + )) + .add("Flex with Margin & Padding", () => { + const margin = number("Equal Margin", 2); + const padding = number("Equal Padding", 2) + return ( + + {`margin: ${margin}, padding: ${padding}`} + + ) + }); diff --git a/examples/react-ui-typescript-storybook/src/components/Flex/Flex.tsx b/examples/react-ui-typescript-storybook/src/components/Flex/Flex.tsx new file mode 100644 index 00000000..231f634f --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/components/Flex/Flex.tsx @@ -0,0 +1,28 @@ +import styled from '../../theme/styled-components' +import { Box } from '../Box/Box' +import { + flexWrap, + FlexWrapProps, + flexDirection, + FlexDirectionProps, + alignItems, + AlignItemsProps, + justifyContent, + JustifyContentProps, + +} from 'styled-system' + +import { themed } from '../../theme/utils' + +export type FlexProps = FlexWrapProps & FlexDirectionProps & AlignItemsProps & JustifyContentProps + +export const Flex = styled(Box)` + display: flex; + ${flexWrap}; + ${flexDirection}; + ${alignItems}; + ${justifyContent}; + ${themed('Flex')}; +` + +Flex.displayName = "Flex" diff --git a/examples/react-ui-typescript-storybook/src/index.ts b/examples/react-ui-typescript-storybook/src/index.ts new file mode 100644 index 00000000..0257e7b4 --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/index.ts @@ -0,0 +1,8 @@ +// Theme +export { theme } from './theme/theme'; + + +// Components +export { Box } from './components/Box/Box'; +export { Flex } from './components/Flex/Flex'; +export { Button } from './components/Button/Button'; diff --git a/examples/react-ui-typescript-storybook/src/theme/styled-components.ts b/examples/react-ui-typescript-storybook/src/theme/styled-components.ts new file mode 100644 index 00000000..3244a531 --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/theme/styled-components.ts @@ -0,0 +1,12 @@ +import * as UIComponents from "styled-components"; +import { ThemeInterface } from './theme' +const { + default: styled, + css, + injectGlobal, + keyframes, + ThemeProvider +} = UIComponents as UIComponents.ThemedStyledComponentsModule as UIComponents.ThemedStyledComponentsModule; + +export { css, injectGlobal, keyframes, ThemeProvider }; +export default styled; diff --git a/examples/react-ui-typescript-storybook/src/theme/theme.ts b/examples/react-ui-typescript-storybook/src/theme/theme.ts new file mode 100644 index 00000000..c253b7bf --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/theme/theme.ts @@ -0,0 +1,74 @@ +import { lighten, darken } from 'polished' + +export const COLOR_PRIMARY = '#17293f'; +export const COLOR_SECONDARY = '#15dffd'; +export const COLOR_GREY = "#b7c1c6"; +export const COLOR_BACKGROUND_GREY = 'whitesmoke'; +export const COLOR_WHITE = 'white'; + +interface FontInterface { + primary: string; + secondary: string; +} + +interface ColorsInterface { + primary: string; + secondary: string; + success: string; + light: string; + lighter: string; + primaryOpaque: string; +} + +interface ShadowsInterface { + small: string; + large: string; +} + +export interface ThemeInterface { + base: number; + font: FontInterface; + colors: ColorsInterface; + space: number[]; + shadows: ShadowsInterface; + buttons: any; // TODO: define variants types +} + +export const theme: ThemeInterface = { + base: 8, + font: { + primary: '"Open Sans", sans-serif', + secondary: 'Baskerville, Times New Roman, Serif, serif' + }, + colors: { + primary: COLOR_PRIMARY, + secondary: COLOR_SECONDARY, + success: 'rgba(45 ,205 ,115 , 1)', + light: 'rgba(245,245,245,1)', + lighter: 'rgba(250 ,250 ,250 , 1)', + primaryOpaque: lighten(0.3, COLOR_PRIMARY) + }, + space: [ + 0, 4, 8, 16, 24, 32, 64, 128, 256, 512 + ], + shadows: { + small: '0 0 4px rgba(0, 0, 0, .125)', + large: '0 0 24px rgba(0, 0, 0, .125)' + }, + buttons: { + primary: { + color: COLOR_WHITE, + backgroundColor: COLOR_PRIMARY, + '&:hover': { + backgroundColor: lighten(0.2, COLOR_PRIMARY), + } + }, + secondary: { + color: COLOR_WHITE, + backgroundColor: COLOR_SECONDARY, + '&:hover': { + backgroundColor: darken(0.2, COLOR_SECONDARY), + } + } + } +}; diff --git a/examples/react-ui-typescript-storybook/src/theme/utils.ts b/examples/react-ui-typescript-storybook/src/theme/utils.ts new file mode 100644 index 00000000..6a021d43 --- /dev/null +++ b/examples/react-ui-typescript-storybook/src/theme/utils.ts @@ -0,0 +1,2 @@ +export const css = (props: {css: any}) => props.css; +export const themed = (key: string) => (props: {theme: {[key: string]: any}}) => props.theme[key];