├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── core ├── README.md ├── if.d.ts ├── package.json ├── src │ ├── If.tsx │ ├── index.tsx │ └── switch.tsx ├── switch.d.ts └── tsconfig.json ├── lerna.json ├── package.json ├── renovate.json ├── test ├── If.test.tsx ├── index.test.tsx └── switch.test.tsx ├── tsconfig.json └── www ├── .kktrc.ts ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── Example.tsx ├── index.tsx └── react-app-env.d.ts └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: jaywcjlove 2 | buy_me_a_coffee: jaywcjlove 3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"] 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build & Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | build-deploy: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | id-token: write 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: 20 18 | registry-url: 'https://registry.npmjs.org' 19 | 20 | - run: npm install 21 | - run: npm run build 22 | - run: npm run doc 23 | - run: npm run coverage 24 | - run: cp -rp ./coverage www/build 25 | 26 | - name: Create Tag 27 | id: create_tag 28 | uses: jaywcjlove/create-tag-action@main 29 | with: 30 | package-path: ./core/package.json 31 | 32 | - name: Generate Contributors Images 33 | uses: jaywcjlove/github-action-contributors@main 34 | with: 35 | filter-author: (renovate\[bot\]|renovate\-bot|dependabot\[bot\]) 36 | output: www/build/CONTRIBUTORS.svg 37 | avatarSize: 42 38 | 39 | - name: Create Coverage Badges 40 | uses: jaywcjlove/coverage-badges-cli@main 41 | with: 42 | output: www/build/badges.svg 43 | 44 | - name: Generate Changelog 45 | id: changelog 46 | uses: jaywcjlove/changelog-generator@main 47 | with: 48 | token: ${{ secrets.GITHUB_TOKEN }} 49 | head-ref: ${{steps.create_tag.outputs.version}} 50 | filter-author: (renovate-bot|Renovate Bot) 51 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' 52 | 53 | - name: Deploy 54 | uses: peaceiris/actions-gh-pages@v4 55 | with: 56 | github_token: ${{ secrets.GITHUB_TOKEN }} 57 | publish_dir: ./www/build 58 | 59 | - name: Create Release 60 | uses: ncipollo/release-action@v1 61 | if: steps.create_tag.outputs.successful 62 | with: 63 | allowUpdates: true 64 | name: ${{ steps.create_tag.outputs.version }} 65 | tag: ${{ steps.create_tag.outputs.version }} 66 | token: ${{ secrets.GITHUB_TOKEN }} 67 | body: | 68 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@uiw/react-only-when@${{steps.changelog.outputs.version}}/file/README.md) [![Coverage Status](https://coveralls.io/repos/github/uiwjs/react-only-when/badge.svg?branch=main)](https://coveralls.io/github/uiwjs/react-only-when?branch=main) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@uiw/react-only-when)](https://www.npmjs.com/package/@uiw/react-only-when) 69 | 70 | ```bash 71 | npm i @uiw/react-only-when@${{steps.changelog.outputs.version}} 72 | ``` 73 | 74 | ${{ steps.changelog.outputs.compareurl }} 75 | ${{ steps.changelog.outputs.changelog }} 76 | 77 | - run: npm publish --access public --provenance 78 | name: 📦 @uiw/react-only-when publish to NPM 79 | continue-on-error: true 80 | working-directory: core 81 | env: 82 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cjs 2 | esm 3 | build 4 | coverage 5 | node_modules 6 | npm-debug.log* 7 | package-lock.json 8 | 9 | .eslintcache 10 | .DS_Store 11 | .cache 12 | .vscode 13 | 14 | *.bak 15 | *.tem 16 | *.temp 17 | #.swp 18 | *.*~ 19 | ~*.* 20 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no-install lint-staged 2 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,jsx,tsx,ts,less,md,json}": [ 3 | "prettier --write" 4 | ] 5 | } -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | **/*.ejs 4 | **/*.yml 5 | package.json 6 | node_modules 7 | dist 8 | build 9 | lib 10 | cjs 11 | esm 12 | test 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 120, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | }, 10 | { 11 | "files": "*.{js,jsx}", 12 | "options": { "parser": "babel" } 13 | }, 14 | { 15 | "files": "*.{ts,tsx}", 16 | "options": { "parser": "babel-ts" } 17 | }, 18 | { 19 | "files": "*.{ts,tsx}", 20 | "options": { "parser": "typescript" } 21 | }, 22 | { 23 | "files": "*.{less,css}", 24 | "options": { "parser": "css" } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 uiw 4 | Copyright (c) sag1v https://github.com/sag1v 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ./core/README.md -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 | react-only-when 2 | === 3 | 4 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) 5 | [![Build & Deploy](https://github.com/uiwjs/react-only-when/actions/workflows/ci.yml/badge.svg)](https://github.com/uiwjs/react-only-when/actions/workflows/ci.yml) 6 | [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@uiw/react-only-when)](https://www.npmjs.com/package/@uiw/react-only-when) 7 | [![npm version](https://img.shields.io/npm/v/@uiw/react-only-when.svg)](https://www.npmjs.com/package/@uiw/react-only-when) 8 | [![Coverage Status](https://uiwjs.github.io/react-only-when/badges.svg)](https://uiwjs.github.io/react-only-when/coverage/lcov-report/) 9 | 10 | A declarative component for conditional rendering. Copy [`react-only-when`](https://github.com/sag1v/react-only-when), let it support TypeScript. 11 | 12 | ## Quick Start 13 | 14 | ```bash 15 | $ npm install --save @uiw/react-only-when 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```jsx 21 | import Only from '@uiw/react-only-when' 22 | 23 | 24 |

Here I Am

25 |
26 | ``` 27 | 28 | ```jsx 29 | import { If } from '@uiw/react-only-when/if' 30 | 31 | 32 |

{props.error}

33 |
34 | ``` 35 | 36 | ```jsx 37 | import { Switch, Case, Default } from '@uiw/react-only-when/switch' 38 | 39 | 40 | preschool 41 | = 6}>primary school 42 | you graduated 43 | 44 | ``` 45 | 46 | ## \ 47 | 48 | React component that renders the children if the `condition` prop is `true`. 49 | 50 | ```jsx 51 | import { If } from '@uiw/react-only-when'; 52 | // Or 53 | import { If } from '@uiw/react-only-when/if' 54 | 55 |
56 | ( 59 |

{props.error}

60 | )} 61 | /> 62 | 63 |

{props.error}

64 |
65 |
66 | ``` 67 | 68 | Or you could just use plain JavaScript: 69 | 70 | ```jsx 71 |
72 | {props.error && ( 73 |

{props.error}

74 | )} 75 |
76 | ``` 77 | 78 | Only Example 79 | 80 | ```jsx mdx:preview&background=#fff&codePen=true 81 | import React, { useState } from 'react'; 82 | import Only from '@uiw/react-only-when'; 83 | 84 | export default function App() { 85 | const [show, setShow] = useState(true) 86 | return ( 87 |
88 | 89 | 90 |

Here I Am

91 |
92 |
93 | ) 94 | } 95 | ``` 96 | 97 | ## \ 98 | 99 | ```jsx 100 | import { Switch, Case, Default } from '@uiw/react-only-when/switch' 101 | 102 | 103 | preschool 104 | = 6}>primary school 105 | you graduated 106 | 107 | ``` 108 | 109 | ```jsx mdx:preview&background=#fff&codePen=true 110 | import React, { useState, Fragment } from 'react'; 111 | import { Switch, Case, Default } from '@uiw/react-only-when/switch' 112 | 113 | export default function App() { 114 | const [age, setAge] = useState(19) 115 | return ( 116 | 117 | setAge(Number(evn.target.value))} /> {age}
118 | 119 | Preschool 120 | = 6 && age < 18}>Primary school 121 | = 18 && age < 60}>Went to college 122 | you graduated 123 | 124 |
125 | ); 126 | } 127 | ``` 128 | 129 | Defaults to specifying a wrapped HTML Element. 130 | 131 | ```jsx mdx:preview&background=#fff&codePen=true 132 | import React, { useState, Fragment } from 'react'; 133 | import { Switch, Case, Default } from '@uiw/react-only-when/switch' 134 | 135 | export default function App() { 136 | const [age, setAge] = useState(19) 137 | return ( 138 | 139 | setAge(Number(evn.target.value))} /> {age} 140 |
141 | 142 | Preschool 143 | = 6 && age < 18}>Primary school 144 | = 18 && age < 60}>Went to college 145 | you graduated 146 | 147 |
148 | ); 149 | } 150 | ``` 151 | 152 | ## `` props 153 | 154 | | prop name | type | default | isRequired | description | 155 | | ----- | ----- | ----- | ----- | ----- | 156 | | children | react element | `null` | `true` | A single child element | 157 | | when | bool | `false` | `true` | When true, children will rendered as is | 158 | | hiddenMode | string | `null` | `false` | Determines how children should be hidden | 159 | | className | string | `w-hidden` | `false` | This is working in combination with `hiddenMode={"css"}` | 160 | 161 | **`hiddenMode` enum** 162 | 163 | | hiddenMode | description | 164 | | ----- | ----- | 165 | | `null` | Will not render the child | 166 | | `display` | Will render the child with `display:none` | 167 | | `visibility` | Will render the child with `visibility:hidden` | 168 | | `css` | Will render the child with a CSS class (you can pass it a custom `className` prop) | 169 | 170 | ## `` Props 171 | 172 | ```tsx 173 | import { ReactElement } from 'react'; 174 | import { FC, PropsWithChildren } from 'react'; 175 | export interface IfProps { 176 | readonly condition?: boolean; 177 | readonly render?: () => ReactElement; 178 | } 179 | export declare const If: FC>; 180 | ``` 181 | 182 | ## `` `` `` Props 183 | 184 | ```tsx 185 | import type { FC, PropsWithChildren } from 'react'; 186 | export const Switch: FC>; 187 | type TagType = React.ElementType | keyof JSX.IntrinsicElements; 188 | interface CaseElementProps { 189 | as?: T; 190 | readonly condition?: boolean; 191 | } 192 | export type CaseProps = CaseElementProps & React.ComponentPropsWithoutRef; 193 | export const Case: (props: CaseProps) => any; 194 | export const Default: (props: Omit, 'condition'>) => import("react/jsx-runtime").JSX.Element; 195 | ``` 196 | 197 | ## Development 198 | 199 | Runs the project in development mode. 200 | 201 | ```bash 202 | # Step 1, run first, listen to the component compile and output the .js file 203 | # listen for compilation output type .d.ts file 204 | npm run watch 205 | # Step 2, development mode, listen to compile preview website instance 206 | npm run start 207 | ``` 208 | 209 | **production** 210 | 211 | Builds the app for production to the build folder. 212 | 213 | ```bash 214 | npm run build 215 | ``` 216 | 217 | The build is minified and the filenames include the hashes. 218 | Your app is ready to be deployed! 219 | 220 | ## Contributors 221 | 222 | As always, thanks to our amazing contributors! 223 | 224 | 225 | 226 | 227 | 228 | Made with [contributors](https://github.com/jaywcjlove/github-action-contributors). 229 | 230 | 231 | ## License 232 | 233 | MIT © [`sag1v`](https://github.com/sag1v) & [`uiwjs`](https://github.com/uiwjs) -------------------------------------------------------------------------------- /core/if.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@uiw/react-only-when/if' { 2 | import { ReactElement } from 'react'; 3 | import { FC, PropsWithChildren } from 'react'; 4 | export interface IfProps { 5 | readonly condition?: boolean; 6 | readonly render?: () => ReactElement; 7 | } 8 | export const If: FC>; 9 | } 10 | -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@uiw/react-only-when", 3 | "version": "3.0.2", 4 | "description": "A declarative component for conditional rendering.", 5 | "main": "cjs/index.js", 6 | "module": "esm/index.js", 7 | "homepage": "https://uiwjs.github.io/react-only-when", 8 | "funding": "https://jaywcjlove.github.io/#/sponsor", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/uiwjs/react-only-when.git" 12 | }, 13 | "exports": { 14 | ".": { 15 | "import": "./esm/index.js", 16 | "require": "./cjs/index.js", 17 | "types": "./esm/index.d.ts" 18 | }, 19 | "./if": { 20 | "import": "./esm/If.js", 21 | "require": "./cjs/If.js", 22 | "types": "./esm/If.d.ts" 23 | }, 24 | "./switch": { 25 | "import": "./esm/switch.js", 26 | "require": "./cjs/switch.js", 27 | "types": "./esm/switch.d.ts" 28 | } 29 | }, 30 | "author": "Kenny Wang", 31 | "license": "MIT", 32 | "peerDependencies": { 33 | "@babel/runtime": ">=7.10.0", 34 | "react": ">=18.0.0", 35 | "react-dom": ">=18.0.0" 36 | }, 37 | "files": [ 38 | "src", 39 | "cjs", 40 | "esm" 41 | ], 42 | "keywords": [ 43 | "component", 44 | "only-when", 45 | "react", 46 | "react-component" 47 | ], 48 | "dependencies": { 49 | "@babel/runtime": "^7.22.6" 50 | }, 51 | "devDependencies": { 52 | "@testing-library/react": "^14.0.0", 53 | "@types/react-test-renderer": "^18.0.0", 54 | "react": "^18.2.0", 55 | "react-dom": "^18.2.0", 56 | "react-test-renderer": "^18.2.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/If.tsx: -------------------------------------------------------------------------------- 1 | import type { ReactElement } from 'react'; 2 | import type { FC, PropsWithChildren } from 'react'; 3 | 4 | export interface IfProps { 5 | readonly condition?: boolean; 6 | readonly render?: () => ReactElement; 7 | } 8 | 9 | export const If: FC> = (props) => 10 | props.condition ? (props.render ? props.render() : (props.children as ReactElement)) : null; 11 | -------------------------------------------------------------------------------- /core/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { type PropsWithChildren } from 'react'; 2 | import { If } from './If'; 3 | 4 | export * from './If'; 5 | 6 | export interface OnlyWhenProps { 7 | /** A single child element */ 8 | children: React.ReactElement; 9 | /** When true, children will rendered as is */ 10 | when: boolean; 11 | /** This is working in combination with hiddenMode={"css"} */ 12 | className?: string; 13 | /** 14 | * Determines how "react-only-when" should hide the child element 15 | * "null": Will not render the child 16 | * "display": Will render the child with display:none 17 | * "visibility": Will render the child with visibility:hidden 18 | * "css": Will render the child with a CSS class (you can pass it a custom className prop) 19 | */ 20 | hiddenMode?: 'null' | 'display' | 'visibility' | 'css'; 21 | } 22 | 23 | export default function OnlyWhen(props: PropsWithChildren) { 24 | const { children, when, hiddenMode = 'null', className = 'w-hidden' } = props; 25 | const singleChild = React.Children.only(children); 26 | const { style, ...restOfChildProps } = singleChild.props; 27 | const extendedProps = { ...restOfChildProps }; 28 | 29 | const keepNode = hiddenMode && hiddenMode !== 'null'; 30 | if (keepNode) { 31 | if (hiddenMode === 'css') { 32 | extendedProps.className = `${extendedProps.className || ''} ${className || ''}`.trim(); 33 | } else { 34 | extendedProps.style = { 35 | ...style, 36 | ...(hiddenMode === 'display' && { display: 'none' }), 37 | ...(hiddenMode === 'visibility' && { visibility: 'hidden' }), 38 | }; 39 | } 40 | } 41 | const cloned = React.cloneElement(singleChild, extendedProps); 42 | const toHide = {cloned}; 43 | 44 | return when ? singleChild : toHide; 45 | } 46 | -------------------------------------------------------------------------------- /core/src/switch.tsx: -------------------------------------------------------------------------------- 1 | import type { FC, PropsWithChildren } from 'react'; 2 | 3 | type Child = typeof Case | typeof Default; 4 | export const Switch: FC> = ({ children }) => { 5 | let matchChild: Child | null = null; 6 | let defaultCase: typeof Default | null = null; 7 | 8 | const childs = Array.isArray(children) ? children : [children]; 9 | childs.some((child) => { 10 | if (!defaultCase && child && child.type === Default) { 11 | defaultCase = child; 12 | } 13 | if (child && child.type === Case) { 14 | const { condition } = child.props; 15 | const conditionIsTrue = Boolean(condition); 16 | if (conditionIsTrue) { 17 | matchChild = child; 18 | return true; 19 | } 20 | } 21 | return false; 22 | }); 23 | return matchChild ?? defaultCase ?? null; 24 | }; 25 | 26 | type TagType = React.ElementType | keyof JSX.IntrinsicElements; 27 | interface CaseElementProps { 28 | as?: T; 29 | readonly condition?: boolean; 30 | } 31 | 32 | export type CaseProps = CaseElementProps & React.ComponentPropsWithoutRef; 33 | 34 | export const Case = (props: CaseProps) => { 35 | const { children, condition, as: Comp, ...reset } = props; 36 | const Elm = Comp as TagType; 37 | return Elm ? {children} : children; 38 | }; 39 | 40 | export const Default = (props: Omit, 'condition'>) => ( 41 | )} /> 42 | ); 43 | -------------------------------------------------------------------------------- /core/switch.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@uiw/react-only-when/switch' { 2 | import type { FC, PropsWithChildren } from 'react'; 3 | export const Switch: FC>; 4 | type TagType = React.ElementType | keyof JSX.IntrinsicElements; 5 | interface CaseElementProps { 6 | as?: T; 7 | readonly condition?: boolean; 8 | } 9 | export type CaseProps = CaseElementProps & React.ComponentPropsWithoutRef; 10 | export const Case: (props: CaseProps) => any; 11 | export const Default: ( 12 | props: Omit, 'condition'>, 13 | ) => import('react/jsx-runtime').JSX.Element; 14 | } 15 | -------------------------------------------------------------------------------- /core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts"], 4 | "compilerOptions": { 5 | "outDir": "../cjs", 6 | "baseUrl": ".", 7 | "noEmit": false, 8 | "paths": { 9 | "*": ["*", "types/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.2", 3 | "packages": ["core", "www"] 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "lerna exec --scope website -- npm run start", 5 | "doc": "lerna exec --scope website -- npm run build", 6 | "⬇️⬇️⬇️⬇️⬇️ package ⬇️⬇️⬇️⬇️⬇️": "▼▼▼▼▼ package ▼▼▼▼▼", 7 | "watch": "lerna exec --scope @uiw/react-only-when -- tsbb watch \"src/*.{ts,tsx}\" --use-babel --cjs cjs", 8 | "build": "lerna exec --scope @uiw/react-only-when -- tsbb build \"src/*.{ts,tsx}\" --use-babel --cjs cjs", 9 | "⬆️⬆️⬆️⬆️⬆️ package ⬆️⬆️⬆️⬆️⬆️": "▲▲▲▲▲ package ▲▲▲▲▲", 10 | "type-check": "tsc --noEmit", 11 | "test": "tsbb test", 12 | "coverage": "tsbb test --coverage --bail", 13 | "prepare": "husky", 14 | "publish": "lerna publish from-package --yes --no-verify-access", 15 | "version": "lerna version --exact --force-publish --no-push --no-git-tag-version", 16 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", 17 | "remove": "npm run clean && lerna exec \"rm -rf package-lock.json\" --scope @uiw/react-only-when --scope website", 18 | "clean": "lerna clean --yes" 19 | }, 20 | "license": "MIT", 21 | "workspaces": [ 22 | "core", 23 | "www" 24 | ], 25 | "engines": { 26 | "node": ">=16.0.0" 27 | }, 28 | "jest": { 29 | "collectCoverageFrom": [ 30 | "/core/src/*.{tsx,ts}" 31 | ], 32 | "coverageReporters": [ 33 | "lcov", 34 | "json-summary" 35 | ] 36 | }, 37 | "overrides": { 38 | "typescript": "^5.1.3" 39 | }, 40 | "devDependencies": { 41 | "husky": "^9.0.0", 42 | "lint-staged": "^15.0.0", 43 | "lerna": "^8.0.0", 44 | "prettier": "^3.0.0", 45 | "tsbb": "^4.5.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "packageRules": [ 4 | { 5 | "matchPackagePatterns": ["*"], 6 | "rangeStrategy": "replace" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/If.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jest/no-conditional-expect */ 2 | import TestRenderer from 'react-test-renderer'; 3 | import { If } from '../core/src/If'; 4 | 5 | describe('', () => { 6 | it('Not rendering children', () => { 7 | const component = TestRenderer.create( 8 | 9 | 10 | , 11 | ); 12 | let only = component.toJSON(); 13 | expect(only).toBeNull(); 14 | }); 15 | 16 | it('rendering children', () => { 17 | const component = TestRenderer.create( 18 | 19 | 20 | , 21 | ); 22 | let only = component.toJSON(); 23 | 24 | if (only && !Array.isArray(only)) { 25 | expect(only.type).toEqual('span'); 26 | expect(only.props.id).toEqual('child'); 27 | } 28 | }); 29 | 30 | it('render props', () => { 31 | const component = TestRenderer.create( } />); 32 | let only = component.toJSON(); 33 | 34 | if (only && !Array.isArray(only)) { 35 | expect(only.type).toEqual('span'); 36 | expect(only.props.id).toEqual('child'); 37 | } 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/index.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jest/no-conditional-expect */ 2 | import TestRenderer from 'react-test-renderer'; 3 | import Only from '../core/src'; 4 | 5 | describe('', () => { 6 | it('Not rendering children', () => { 7 | const component = TestRenderer.create( 8 | 9 | 10 | , 11 | ); 12 | 13 | let only = component.toJSON(); 14 | expect(only).toBeNull(); 15 | }); 16 | 17 | it('Not touching the style or className', () => { 18 | const classNameToTest = 'test-className'; 19 | const component = TestRenderer.create( 20 | 21 | 22 | , 23 | ); 24 | 25 | let only = component.toJSON(); 26 | if (only && !Array.isArray(only)) { 27 | expect(only.type).toEqual('span'); 28 | expect(only.props.id).toEqual('child'); 29 | expect(only.props.children).toBeUndefined(); 30 | expect(only.props.className).toEqual(classNameToTest); 31 | expect(only.props.style).toEqual({ color: 'green' }); 32 | } 33 | }); 34 | 35 | it('Adding style visibility (without deleting other styles', () => { 36 | const component = TestRenderer.create( 37 | 38 | 39 | , 40 | ); 41 | 42 | let only = component.toJSON(); 43 | if (only && !Array.isArray(only)) { 44 | expect(only.type).toEqual('span'); 45 | expect(only.props.id).toEqual('child'); 46 | expect(only.props.children).toBeUndefined(); 47 | expect(only.props.style).toEqual({ color: 'green', visibility: 'hidden' }); 48 | } 49 | }); 50 | 51 | it('Adding style display (without deleting other styles)', () => { 52 | const component = TestRenderer.create( 53 | 54 | 55 | , 56 | ); 57 | 58 | let only = component.toJSON(); 59 | if (only && !Array.isArray(only)) { 60 | expect(only.type).toEqual('span'); 61 | expect(only.props.id).toEqual('child'); 62 | expect(only.props.children).toBeUndefined(); 63 | expect(only.props.style).toEqual({ color: 'green', display: 'none' }); 64 | } 65 | }); 66 | 67 | it('Joining className (default class)', () => { 68 | const testClassName = 'test-className'; 69 | const component = TestRenderer.create( 70 | 71 | 72 | , 73 | ); 74 | 75 | let only = component.toJSON(); 76 | if (only && !Array.isArray(only)) { 77 | expect(only.type).toEqual('span'); 78 | expect(only.props.id).toEqual('child'); 79 | expect(only.props.children).toBeUndefined(); 80 | expect(only.props.className).toEqual('test-className w-hidden'); 81 | expect(only.props.style).toEqual({ color: 'green' }); 82 | } 83 | }); 84 | 85 | it('Joining className (as prop)', () => { 86 | const testClassName = 'test-className'; 87 | const component = TestRenderer.create( 88 | 89 | 90 | , 91 | ); 92 | 93 | let only = component.toJSON(); 94 | if (only && !Array.isArray(only)) { 95 | expect(only.type).toEqual('span'); 96 | expect(only.props.id).toEqual('child'); 97 | expect(only.props.children).toBeUndefined(); 98 | expect(only.props.className).toEqual('test-className www'); 99 | expect(only.props.style).toEqual({ color: 'green' }); 100 | } 101 | }); 102 | 103 | it('Joining className default value', () => { 104 | const component = TestRenderer.create( 105 | 106 | 107 | , 108 | ); 109 | 110 | let only = component.toJSON(); 111 | if (only && !Array.isArray(only)) { 112 | expect(only.type).toEqual('span'); 113 | expect(only.props.id).toEqual('child'); 114 | expect(only.props.children).toBeUndefined(); 115 | expect(only.props.className).toEqual('w-hidden'); 116 | expect(only.props.style).toEqual({ color: 'green' }); 117 | } 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /test/switch.test.tsx: -------------------------------------------------------------------------------- 1 | import renderer from 'react-test-renderer'; 2 | import { render, screen } from '@testing-library/react'; 3 | import { Switch, Case, Default } from '../core/src/switch'; 4 | 5 | it('', () => { 6 | const component = renderer.create( 7 | 8 | ); 9 | const only = component.toJSON(); 10 | expect(only).toBeNull(); 11 | }); 12 | 13 | it('', () => { 14 | const { container } = render( 15 | 16 | you graduated 17 | 18 | ); 19 | expect(container.innerHTML).toEqual('you graduated'); 20 | }); 21 | 22 | it('', () => { 23 | const { container } = render( 24 | 25 | preschool 26 | you graduated 27 | 28 | ); 29 | expect(container.innerHTML).toEqual('preschool'); 30 | }); 31 | 32 | it(' 1', () => { 33 | const { container } = render( 34 | 35 | preschool 36 | primary school 37 | you graduated 38 | 39 | ); 40 | expect(container.innerHTML).toEqual('preschool'); 41 | }); 42 | 43 | it(' 2', () => { 44 | const { container } = render( 45 | 46 | preschool 47 | primary school 48 | you graduated 49 | 50 | ); 51 | expect(container.innerHTML).toEqual('you graduated'); 52 | }); 53 | 54 | 55 | it('', () => { 56 | render( 57 | 58 | preschool 59 | 60 | ); 61 | const span = screen.getByTestId('span'); 62 | expect(span.tagName).toEqual('SPAN'); 63 | expect(span.innerHTML).toEqual('preschool'); 64 | }); 65 | 66 | 67 | it('', () => { 68 | render( 69 | 70 | you graduated 71 | 72 | ); 73 | const elm = screen.getByTestId('elm'); 74 | expect(elm.tagName).toEqual('P'); 75 | expect(elm.innerHTML).toEqual('you graduated'); 76 | expect(elm.title).toEqual('test case'); 77 | }); 78 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "declaration": true, 16 | "baseUrl": ".", 17 | "jsx": "react-jsx", 18 | "noFallthroughCasesInSwitch": true, 19 | "noEmit": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /www/.kktrc.ts: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import { LoaderConfOptions, WebpackConfiguration } from 'kkt'; 3 | import rawModules from '@kkt/raw-modules'; 4 | import { disableScopePlugin } from '@kkt/scope-plugin-options'; 5 | import { mdCodeModulesLoader } from 'markdown-react-code-preview-loader'; 6 | import pkg from './package.json'; 7 | 8 | export default (conf: WebpackConfiguration, env: 'production' | 'development', options: LoaderConfOptions) => { 9 | conf = rawModules(conf, env, { ...options }); 10 | conf = mdCodeModulesLoader(conf); 11 | conf = disableScopePlugin(conf); 12 | // Get the project version. 13 | conf.plugins!.push( 14 | new webpack.DefinePlugin({ 15 | VERSION: JSON.stringify(pkg.version), 16 | }), 17 | ); 18 | conf.module!.exprContextCritical = false; 19 | conf.ignoreWarnings = [ 20 | { 21 | module: /node_modules[\\/]parse5[\\/]/, 22 | }, 23 | ]; 24 | if (env === 'production') { 25 | conf.output = { ...conf.output, publicPath: './' }; 26 | conf.optimization = { 27 | ...conf.optimization, 28 | splitChunks: { 29 | automaticNameDelimiter: '.', 30 | maxSize: 500000, 31 | minSize: 100000, 32 | cacheGroups: { 33 | reactvendor: { 34 | test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, 35 | name: 'react-vendor', 36 | reuseExistingChunk: true, 37 | chunks: 'all', 38 | priority: -10, 39 | }, 40 | refractor: { 41 | test: /[\\/]node_modules[\\/](refractor)[\\/]/, 42 | name: 'refractor-prismjs-vendor', 43 | chunks: 'all', 44 | }, 45 | }, 46 | }, 47 | }; 48 | } 49 | return conf; 50 | }; 51 | -------------------------------------------------------------------------------- /www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "3.0.2", 4 | "preview": true, 5 | "scripts": { 6 | "build": "kkt build", 7 | "start": "kkt start", 8 | "map": "source-map-explorer build/static/js/*.js --html build/website-result.html" 9 | }, 10 | "dependencies": { 11 | "@uiw/react-markdown-preview-example": "^2.1.4", 12 | "@uiw/react-only-when": "3.0.2", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 18 | "@kkt/raw-modules": "^7.5.1", 19 | "@kkt/scope-plugin-options": "^7.5.1", 20 | "@types/react": "^18.0.31", 21 | "@types/react-dom": "^18.0.11", 22 | "kkt": "^7.5.1", 23 | "markdown-react-code-preview-loader": "^2.1.2", 24 | "source-map-explorer": "^2.5.3" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /www/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uiwjs/react-only-when/e6b9d56992958f962ac995410345d7ae4910478c/www/public/favicon.ico -------------------------------------------------------------------------------- /www/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | React App - A declarative component for conditional rendering. 20 | 21 | 22 | 23 | 26 |
27 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /www/src/Example.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import Only, { OnlyWhenProps } from '@uiw/react-only-when'; 3 | 4 | type CheckboxProps = { 5 | hiddenMode?: OnlyWhenProps['hiddenMode']; 6 | value?: OnlyWhenProps['hiddenMode']; 7 | onChange?: (evn: React.ChangeEvent) => void; 8 | }; 9 | 10 | function Checkbox(props: CheckboxProps) { 11 | const { hiddenMode, value, onChange } = props; 12 | return ( 13 | 17 | ); 18 | } 19 | 20 | const App: React.FC = () => { 21 | const [when, setWhen] = useState(true); 22 | const [hiddenMode, setHiddenMode] = useState('null'); 23 | return ( 24 |
25 | 28 |
29 | setHiddenMode('null')} /> 30 | setHiddenMode('display')} /> 31 | setHiddenMode('visibility')} /> 32 | setHiddenMode('css')} /> 33 |
34 | 35 |

Here I Am

36 |
37 |
38 | ); 39 | }; 40 | 41 | export default App; 42 | -------------------------------------------------------------------------------- /www/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client'; 2 | import MarkdownPreviewExample from '@uiw/react-markdown-preview-example'; 3 | import data from '../../core/README.md'; 4 | import pkg from '../../core/package.json'; 5 | import OnlyWhenExample from './Example'; 6 | 7 | const Github = MarkdownPreviewExample.Github; 8 | const Example = MarkdownPreviewExample.Example; 9 | 10 | const container = document.getElementById('root'); 11 | const root = createRoot(container!); 12 | root.render( 13 | 21 | 22 | 23 | 24 | 25 | , 26 | ); 27 | -------------------------------------------------------------------------------- /www/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare var VERSION: string; 4 | 5 | declare module '*.md' { 6 | import { CodeBlockData } from 'markdown-react-code-preview-loader'; 7 | const src: CodeBlockData; 8 | export default src; 9 | } 10 | -------------------------------------------------------------------------------- /www/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "include": ["src/*", ".kktrc.ts"], 4 | "compilerOptions": { 5 | "noEmit": false 6 | } 7 | } 8 | --------------------------------------------------------------------------------