mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2026-03-09 22:22:42 +03:00
Compare commits
No commits in common. "main" and "@redux-devtools/app@1.0.0-5" have entirely different histories.
main
...
@redux-dev
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@1.6.4/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": [],
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
||||
}
|
||||
}
|
||||
|
|
@ -7,5 +7,3 @@ build
|
|||
coverage
|
||||
node_modules
|
||||
__snapshots__
|
||||
storybook-static
|
||||
.vscode/*
|
||||
|
|
|
|||
28
.eslintrc
Normal file
28
.eslintrc
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"root": true,
|
||||
"parser": "babel-eslint",
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended", "prettier"],
|
||||
"globals": {
|
||||
"chrome": true
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"jest": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"eol-last": ["warn"],
|
||||
"max-len": ["warn", { "code": 120, "ignoreComments": true }],
|
||||
"quotes": ["warn", "single", "avoid-escape"],
|
||||
"jsx-quotes": ["warn", "prefer-double"],
|
||||
"react/prop-types": 0,
|
||||
"prettier/prettier": "error"
|
||||
},
|
||||
"plugins": ["prettier", "react", "babel"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
.gitattributes
vendored
9
.gitattributes
vendored
|
|
@ -1 +1,8 @@
|
|||
* text=auto eol=lf
|
||||
*.js text eol=lf
|
||||
*.jsx text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.tsx text eol=lf
|
||||
*.json text eol=lf
|
||||
*.css text eol=lf
|
||||
*.html text eol=lf
|
||||
*.md text eol=lf
|
||||
|
|
|
|||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
|
|
@ -1,2 +1 @@
|
|||
github: Methuselah96
|
||||
open_collective: redux-devtools-extension
|
||||
|
|
|
|||
31
.github/workflows/CI.yml
vendored
31
.github/workflows/CI.yml
vendored
|
|
@ -1,31 +0,0 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: 'ubuntu-22.04'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Check formatting
|
||||
run: pnpm run format:check
|
||||
- name: Build
|
||||
run: pnpm run build:all
|
||||
- name: Lint
|
||||
run: pnpm run lint:all
|
||||
- name: Test
|
||||
uses: coactions/setup-xvfb@v1
|
||||
with:
|
||||
run: pnpm run test:all
|
||||
58
.github/workflows/release.yml
vendored
58
.github/workflows/release.yml
vendored
|
|
@ -1,58 +0,0 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: 'ubuntu-22.04'
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
version: pnpm changeset version
|
||||
publish: pnpm run release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Archive Chrome Extension
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: chrome
|
||||
path: extension/chrome/dist
|
||||
|
||||
- name: Archive Edge Extension
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: edge
|
||||
path: extension/edge/dist
|
||||
|
||||
- name: Archive Firefox Extension
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: firefox
|
||||
path: extension/firefox/dist
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,4 +9,3 @@ coverage
|
|||
.idea
|
||||
.eslintcache
|
||||
!packages/redux-devtools-slider-monitor/examples/todomvc/dist/index.html
|
||||
.nx
|
||||
|
|
|
|||
|
|
@ -8,7 +8,3 @@ coverage
|
|||
node_modules
|
||||
__snapshots__
|
||||
dev
|
||||
**/demo/public/**
|
||||
storybook-static
|
||||
.vscode/*
|
||||
pnpm-lock.yaml
|
||||
|
|
|
|||
13
.travis.yml
Normal file
13
.travis.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 'stable'
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- 'node_modules'
|
||||
script:
|
||||
- yarn build:all
|
||||
- yarn lint:all
|
||||
- yarn prettier:check
|
||||
- yarn test:all
|
||||
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Change log
|
||||
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
See all notable changes on [Releases](https://github.com/gaearon/redux-devtools/releases) page.
|
||||
21
LICENSE.md
21
LICENSE.md
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Dan Abramov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
99
README.md
99
README.md
|
|
@ -1,100 +1,25 @@
|
|||

|
||||
[](https://github.com/reduxjs/redux-devtools/pulls)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
[](https://travis-ci.org/reduxjs/redux-devtools) [](https://github.com/reduxjs/redux-devtools/pulls)
|
||||
|
||||
# Redux DevTools
|
||||
|
||||
Developer Tools to power-up [Redux](https://redux.js.org/) development workflow or any other architecture which handles the state change (see [integrations](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Integrations.md)).
|
||||
Developer Tools to power-up [Redux](https://github.com/reactjs/redux) development workflow or any other architecture which handles the state change (see [integrations](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Integrations.md)).
|
||||
|
||||
It can be used as a browser extension (for [Chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd), [Edge](https://microsoftedge.microsoft.com/addons/detail/redux-devtools/nnkgneoiohoecpdiaponcejilbhhikei) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/)), as [a standalone app](https://github.com/reduxjs/redux-devtools/tree/main/packages/redux-devtools-app) or as [a React component](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools) integrated in the client app.
|
||||
It can be used as a browser extension (for [Chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd), [Edge](https://microsoftedge.microsoft.com/addons/detail/redux-devtools/nnkgneoiohoecpdiaponcejilbhhikei) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/)), as [a standalone app](https://github.com/zalmoxisus/remotedev-app) or as [a React component](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools) integrated in the client app.
|
||||
|
||||

|
||||
|
||||
## Documentation
|
||||
> Note that this repository is work in progress for [the monorepo](https://github.com/reduxjs/redux-devtools/issues/412), which will merge all the packages. Please refer to [Redux DevTools Extension](https://github.com/zalmoxisus/redux-devtools-extension) and [Redux DevTools package](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools).
|
||||
|
||||
- [Browser Extension Installation and Configuration](https://github.com/reduxjs/redux-devtools/tree/main/extension#installation)
|
||||
### Documentation
|
||||
|
||||
- [Browser Extension Installation and Configuration](https://github.com/zalmoxisus/redux-devtools-extension#installation)
|
||||
- [Manual Integration as a React Component](./docs/Walkthrough.md#manual-integration)
|
||||
- [Extension Options (Arguments)](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md)
|
||||
- [Extension Methods (Advanced API)](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Methods.md)
|
||||
- [Extension Options (Arguments)](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/API/Arguments.md)
|
||||
- [Extension Methods (Advanced API)](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/API/Methods.md)
|
||||
- [Remote monitoring](./docs/Integrations/Remote.md)
|
||||
- [Troubleshooting](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Troubleshooting.md)
|
||||
- [Recipes](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Recipes.md)
|
||||
- [FAQ](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/FAQ.md)
|
||||
|
||||
## Development
|
||||
|
||||
This is a monorepo powered by [pnpm](https://pnpm.io/). [Install pnpm](https://pnpm.io/installation) and run `pnpm install` to get started. Each package's dependencies need to be built before the package itself can be built. You can either build all the packages (i.e., `pnpm run build:all`) or use pnpm workspace commands to build only the packages necessary for the packages you're working on (i.e., `pnpm --filter "remotedev-redux-devtools-extension" build`).
|
||||
|
||||
## Backers
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/redux-devtools-extension#backer)]
|
||||
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/0/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/1/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/2/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/3/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/4/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/5/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/6/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/7/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/8/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/9/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/10/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/11/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/12/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/13/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/14/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/15/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/16/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/17/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/18/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/19/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/20/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/21/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/22/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/23/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/24/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/25/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/26/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/27/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/28/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/29/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/29/avatar.svg"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/redux-devtools-extension#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/0/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/1/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/2/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/3/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/4/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/5/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/6/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/7/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/8/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/9/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/10/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/11/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/12/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/13/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/14/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/15/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/16/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/17/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/18/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/19/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/20/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/21/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/22/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/23/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/24/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/25/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/26/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/27/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/28/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/29/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/29/avatar.svg"></a>
|
||||
- [Troubleshooting](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/Troubleshooting.md)
|
||||
- [Recipes](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/Recipes.md)
|
||||
- [FAQ](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/FAQ.md)
|
||||
|
||||
### License
|
||||
|
||||
|
|
|
|||
14
babel.config.js
Executable file
14
babel.config.js
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
module.exports = {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-flow'],
|
||||
plugins: [
|
||||
[
|
||||
'@babel/plugin-transform-runtime',
|
||||
{
|
||||
regenerator: true,
|
||||
},
|
||||
],
|
||||
['@babel/plugin-proposal-decorators', { legacy: true }],
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
],
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
## Remote monitoring
|
||||
|
||||
By installing [`@redux-devtools/cli`](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-cli#usage), starting the server server and launching the Redux DevTools app (`redux-devtools --open`), you can connect any remote application, even not javascript. There are some integrations for javascript like [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools) and [remotedev](https://github.com/zalmoxisus/remotedev), but the plan is to deprecate them and support it out of the box from the extension without a websocket server. It is more useful for non-js apps.
|
||||
By installing [`redux-devtools-cli`](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-cli#usage), starting the server server and launching the Redux DevTools app (`redux-devtools --open`), you can connect any remote application, even not javascript. There are some integrations for javascript like [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools) and [remotedev](https://github.com/zalmoxisus/remotedev), but the plan is to deprecate them and support it out of the box from the extension without a websocket server. It is more useful for non-js apps.
|
||||
|
||||
### WebSocket Clients
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Browser Extension
|
||||
|
||||
If you don’t want to bother with installing Redux DevTools and integrating it into your project, consider using [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools/tree/master/extension) for Chrome and Firefox. It provides access to the most popular monitors, is easy to configure to filter actions, and doesn’t require installing any packages.
|
||||
If you don’t want to bother with installing Redux DevTools and integrating it into your project, consider using [Redux DevTools Extension](https://github.com/zalmoxisus/redux-devtools-extension) for Chrome and Firefox. It provides access to the most popular monitors, is easy to configure to filter actions, and doesn’t require installing any packages.
|
||||
|
||||
## Manual Integration
|
||||
|
||||
|
|
@ -12,14 +12,14 @@ It’s more steps, but you will have full control over monitors and their config
|
|||
### Installation
|
||||
|
||||
```
|
||||
npm install --save-dev @redux-devtools/core
|
||||
npm install --save-dev redux-devtools
|
||||
```
|
||||
|
||||
You’ll also likely want to install some monitors:
|
||||
|
||||
```
|
||||
npm install --save-dev @redux-devtools/log-monitor
|
||||
npm install --save-dev @redux-devtools/dock-monitor
|
||||
npm install --save-dev redux-devtools-log-monitor
|
||||
npm install --save-dev redux-devtools-dock-monitor
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
|
@ -34,11 +34,11 @@ Somewhere in your project, create a `DevTools` component by passing a `monitor`
|
|||
import React from 'react';
|
||||
|
||||
// Exported from redux-devtools
|
||||
import { createDevTools } from '@redux-devtools/core';
|
||||
import { createDevTools } from 'redux-devtools';
|
||||
|
||||
// Monitors are separate packages, and you can make a custom one
|
||||
import LogMonitor from '@redux-devtools/log-monitor';
|
||||
import DockMonitor from '@redux-devtools/dock-monitor';
|
||||
import LogMonitor from 'redux-devtools-log-monitor';
|
||||
import DockMonitor from 'redux-devtools-dock-monitor';
|
||||
|
||||
// createDevTools takes a monitor and produces a DevTools component
|
||||
const DevTools = createDevTools(
|
||||
|
|
@ -52,7 +52,7 @@ const DevTools = createDevTools(
|
|||
defaultIsVisible={true}
|
||||
>
|
||||
<LogMonitor theme="tomorrow" />
|
||||
</DockMonitor>,
|
||||
</DockMonitor>
|
||||
);
|
||||
|
||||
export default DevTools;
|
||||
|
|
@ -88,7 +88,7 @@ const enhancer = compose(
|
|||
// Middleware you want to use in development:
|
||||
applyMiddleware(d1, d2, d3),
|
||||
// Required! Enable Redux DevTools with the monitors you chose
|
||||
DevTools.instrument(),
|
||||
DevTools.instrument()
|
||||
);
|
||||
|
||||
export default function configureStore(initialState) {
|
||||
|
|
@ -100,8 +100,8 @@ export default function configureStore(initialState) {
|
|||
if (module.hot) {
|
||||
module.hot.accept('../reducers', () =>
|
||||
store.replaceReducer(
|
||||
require('../reducers') /*.default if you use Babel 6+ */,
|
||||
),
|
||||
require('../reducers') /*.default if you use Babel 6+ */
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ If you’d like, you may add another store enhancer called `persistState()`. It
|
|||
|
||||
```js
|
||||
// ...
|
||||
import { persistState } from '@redux-devtools/core';
|
||||
import { persistState } from 'redux-devtools';
|
||||
|
||||
const enhancer = compose(
|
||||
// Middleware you want to use in development:
|
||||
|
|
@ -121,7 +121,7 @@ const enhancer = compose(
|
|||
// Required! Enable Redux DevTools with the monitors you chose
|
||||
DevTools.instrument(),
|
||||
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
||||
persistState(getDebugSessionKey()),
|
||||
persistState(getDebugSessionKey())
|
||||
);
|
||||
|
||||
function getDebugSessionKey() {
|
||||
|
|
@ -190,7 +190,7 @@ export default function configureStore(initialState) {
|
|||
|
||||
```js
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import { persistState } from '@redux-devtools/core';
|
||||
import { persistState } from 'redux-devtools';
|
||||
import rootReducer from '../reducers';
|
||||
import DevTools from '../containers/DevTools';
|
||||
|
||||
|
|
@ -200,7 +200,7 @@ const enhancer = compose(
|
|||
// Required! Enable Redux DevTools with the monitors you chose
|
||||
DevTools.instrument(),
|
||||
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
||||
persistState(getDebugSessionKey()),
|
||||
persistState(getDebugSessionKey())
|
||||
);
|
||||
|
||||
function getDebugSessionKey() {
|
||||
|
|
@ -219,8 +219,8 @@ export default function configureStore(initialState) {
|
|||
if (module.hot) {
|
||||
module.hot.accept('../reducers', () =>
|
||||
store.replaceReducer(
|
||||
require('../reducers') /*.default if you use Babel 6+ */,
|
||||
),
|
||||
require('../reducers') /*.default if you use Babel 6+ */
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +333,7 @@ render(
|
|||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById('root'),
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
|
|
@ -353,7 +353,7 @@ export default function showDevTools(store) {
|
|||
const popup = window.open(
|
||||
null,
|
||||
'Redux DevTools',
|
||||
'menubar=no,location=no,resizable=yes,scrollbars=no,status=no',
|
||||
'menubar=no,location=no,resizable=yes,scrollbars=no,status=no'
|
||||
);
|
||||
// Reload in case it already exists
|
||||
popup.location.reload();
|
||||
|
|
@ -362,7 +362,7 @@ export default function showDevTools(store) {
|
|||
popup.document.write('<div id="react-devtools-root"></div>');
|
||||
render(
|
||||
<DevTools store={store} />,
|
||||
popup.document.getElementById('react-devtools-root'),
|
||||
popup.document.getElementById('react-devtools-root')
|
||||
);
|
||||
}, 10);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
import { defineConfig } from 'eslint/config';
|
||||
import eslint from '@eslint/js';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default defineConfig([eslint.configs.recommended, eslintConfigPrettier]);
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import { defineConfig } from 'eslint/config';
|
||||
import eslint from '@eslint/js';
|
||||
import react from 'eslint-plugin-react';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import jest from 'eslint-plugin-jest';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...react.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...reactHooks.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...jest.configs['flat/recommended'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...jest.configs['jest/style'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
]);
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
import { defineConfig } from 'eslint/config';
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (tsconfigRootDir, files = ['**/*.ts'], project = true) =>
|
||||
defineConfig([
|
||||
{
|
||||
files,
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project,
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files,
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files,
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
import { defineConfig } from 'eslint/config';
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import jest from 'eslint-plugin-jest';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (tsconfigRootDir) =>
|
||||
defineConfig([
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts'],
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts'],
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.test.json'],
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...jest.configs['flat/recommended'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...jest.configs['jest/style'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
import { defineConfig } from 'eslint/config';
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import react from 'eslint-plugin-react';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (
|
||||
tsconfigRootDir,
|
||||
files = ['**/*.ts', '**/*.tsx'],
|
||||
project = true,
|
||||
) =>
|
||||
defineConfig([
|
||||
{
|
||||
files,
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project,
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files,
|
||||
...react.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files,
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files,
|
||||
...reactHooks.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files,
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files,
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/no-misused-promises': [
|
||||
'error',
|
||||
{
|
||||
checksVoidReturn: {
|
||||
attributes: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
'react/prop-types': 'off',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
import { defineConfig } from 'eslint/config';
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import react from 'eslint-plugin-react';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import jest from 'eslint-plugin-jest';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (tsconfigRootDir) =>
|
||||
defineConfig([
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.test.json'],
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...react.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...reactHooks.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...jest.configs['flat/recommended'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...jest.configs['jest/style'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
},
|
||||
},
|
||||
]);
|
||||
18
eslintrc.ts.base.json
Normal file
18
eslintrc.ts.base.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||
}
|
||||
}
|
||||
19
eslintrc.ts.jest.base.json
Normal file
19
eslintrc.ts.jest.base.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"plugins": ["jest"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:jest/recommended",
|
||||
"plugin:jest/style",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||
}
|
||||
}
|
||||
30
eslintrc.ts.react.base.json
Normal file
30
eslintrc.ts.react.base.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "react"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:react/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"prettier/react"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||
}
|
||||
}
|
||||
21
eslintrc.ts.react.jest.base.json
Normal file
21
eslintrc.ts.react.jest.base.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"plugins": ["jest"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:react/recommended",
|
||||
"plugin:jest/recommended",
|
||||
"plugin:jest/style",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"prettier/react"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||
}
|
||||
}
|
||||
7
extension/.babelrc
Normal file
7
extension/.babelrc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||
["@babel/plugin-proposal-class-properties", { "loose": true }]
|
||||
]
|
||||
}
|
||||
8
extension/.bookignore
Normal file
8
extension/.bookignore
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
src/
|
||||
build/
|
||||
dev/
|
||||
examples/
|
||||
npm-package/
|
||||
test/
|
||||
package.json
|
||||
webpack/
|
||||
7
extension/.eslintignore
Normal file
7
extension/.eslintignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
node_modules
|
||||
build
|
||||
dev
|
||||
webpack/replace
|
||||
examples
|
||||
npm-package
|
||||
_book
|
||||
31
extension/.eslintrc
Normal file
31
extension/.eslintrc
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"root": true,
|
||||
"extends": "eslint-config-airbnb",
|
||||
"globals": {
|
||||
"chrome": true,
|
||||
"__DEVELOPMENT__": true
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"react/react-in-jsx-scope": 2,
|
||||
"react/jsx-quotes": 0,
|
||||
"block-scoped-var": 0,
|
||||
"padded-blocks": 0,
|
||||
"quotes": [1, "single"],
|
||||
"comma-style": [2, "last"],
|
||||
"no-use-before-define": [0, "nofunc"],
|
||||
"func-names": 0,
|
||||
"prefer-const": 0,
|
||||
"comma-dangle": 0,
|
||||
"id-length": 0,
|
||||
"indent": [2, 2, { "SwitchCase": 1 }],
|
||||
"new-cap": [2, { "capIsNewExceptions": ["Test"] }],
|
||||
"default-case": 0
|
||||
},
|
||||
"plugins": ["react"]
|
||||
}
|
||||
9
extension/.gitignore
vendored
9
extension/.gitignore
vendored
|
|
@ -1,2 +1,9 @@
|
|||
node_modules
|
||||
dist
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
.idea/
|
||||
dist/
|
||||
build/
|
||||
dev/
|
||||
tmp/
|
||||
_book
|
||||
|
|
|
|||
32
extension/.travis.yml
Normal file
32
extension/.travis.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
sudo: required
|
||||
dist: trusty
|
||||
language: node_js
|
||||
node_js:
|
||||
- '6'
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.yarn-cache
|
||||
- node_modules
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- google-chrome
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
- g++-4.8
|
||||
|
||||
install:
|
||||
- '/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16'
|
||||
- npm install -g yarn
|
||||
- yarn install
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start &
|
||||
- sleep 3
|
||||
|
||||
script:
|
||||
- yarn test
|
||||
|
|
@ -1,265 +1,4 @@
|
|||
# remotedev-redux-devtools-extension
|
||||
# Change Log
|
||||
|
||||
## 3.2.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3f90241]
|
||||
- Updated dependencies [d61d31a]
|
||||
- Updated dependencies [804e729]
|
||||
- Updated dependencies [12849a4]
|
||||
- Updated dependencies [804d6bd]
|
||||
- Updated dependencies [6481386]
|
||||
- @redux-devtools/instrument@3.0.0
|
||||
- @redux-devtools/ui@3.0.0
|
||||
- @redux-devtools/slider-monitor@7.0.0
|
||||
- @redux-devtools/core@5.0.0
|
||||
- @redux-devtools/app@8.0.0
|
||||
- @redux-devtools/serialize@1.0.0
|
||||
- @redux-devtools/utils@4.0.0
|
||||
|
||||
## 3.2.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6163276]
|
||||
- @redux-devtools/app@7.0.0
|
||||
- @redux-devtools/slider-monitor@6.0.0
|
||||
- @redux-devtools/ui@2.0.0
|
||||
|
||||
## 3.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @redux-devtools/app@6.2.2
|
||||
|
||||
## 3.2.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [91f21b2]
|
||||
- @redux-devtools/core@4.1.1
|
||||
- @redux-devtools/slider-monitor@5.1.1
|
||||
- @redux-devtools/utils@3.1.1
|
||||
- @redux-devtools/app@6.2.1
|
||||
|
||||
## 3.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6830118]
|
||||
- react-json-tree@0.20.0
|
||||
- @redux-devtools/app@6.2.0
|
||||
- @redux-devtools/slider-monitor@6.0.0
|
||||
- @redux-devtools/ui@1.4.0
|
||||
- @redux-devtools/core@4.1.0
|
||||
- @redux-devtools/utils@4.0.0
|
||||
|
||||
## 3.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b25bf13: Send state from background when monitor connects
|
||||
|
||||
## 3.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 50d7682: Fix DevTools from losing connection
|
||||
|
||||
## 3.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- eb3ac09: Add logging to background service worker
|
||||
|
||||
## 3.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f1d6158: Fix mocking Chrome API for Electron
|
||||
|
||||
## 3.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- fd9f950: Fix monitoring on opening panel
|
||||
- e49708d: Fix manifest.json for Edge
|
||||
|
||||
## 3.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- abd03a7: Fix: only send data to extension if DevTools are open
|
||||
|
||||
## 3.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 83b2c19: Upgrade to Manifest V3
|
||||
|
||||
## 3.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 73688e1: Fix releasing Firefox extension
|
||||
|
||||
## 3.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2163bc3: Split large messages sent from background page to devpanel
|
||||
|
||||
## 3.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bbb1a40]
|
||||
- react-json-tree@0.19.0
|
||||
- @redux-devtools/slider-monitor@5.0.1
|
||||
- @redux-devtools/ui@1.3.2
|
||||
|
||||
## 3.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 191d419: Convert d3 packages to ESM
|
||||
- Updated dependencies [191d419]
|
||||
- @redux-devtools/app@6.0.1
|
||||
|
||||
## 3.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5cfe3e5]
|
||||
- Updated dependencies [decc035]
|
||||
- @redux-devtools/app@6.0.0
|
||||
- @redux-devtools/slider-monitor@5.0.0
|
||||
- @redux-devtools/core@4.0.0
|
||||
- @redux-devtools/utils@3.0.0
|
||||
|
||||
## 3.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [158ba2c]
|
||||
- @redux-devtools/app@5.0.0
|
||||
|
||||
## 3.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 65205f90: Replace Action<unknown> with Action<string>
|
||||
- Updated dependencies [65205f90]
|
||||
- @redux-devtools/app@4.0.1
|
||||
- @redux-devtools/core@3.13.2
|
||||
|
||||
## 3.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e57bcb39]
|
||||
- @redux-devtools/app@4.0.0
|
||||
|
||||
## 3.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bca76009: Fix missing CSS for code editor
|
||||
|
||||
## 3.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 64ed81b0: Fix extension in Firefox and Chrome Incognito
|
||||
|
||||
## 3.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d18525b5: Increase min-width of popup
|
||||
- Updated dependencies [57751ff9]
|
||||
- @redux-devtools/app@3.0.0
|
||||
|
||||
## 3.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- d54adb76: Option to sort State Tree keys alphabetically
|
||||
Option to disable collapsing of object keys
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @redux-devtools/app@2.2.2
|
||||
|
||||
## 3.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 450cde6e: Fix responsive layout
|
||||
|
||||
## 3.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [81926f32]
|
||||
- react-json-tree@0.18.0
|
||||
- @redux-devtools/app@2.2.1
|
||||
|
||||
## 3.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1aa6c4f7: Fix remounting root for devpanel
|
||||
|
||||
## 3.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 20ebf725: Remove source map from page wrap bundle
|
||||
|
||||
## 3.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 24f60a7a: bump min popup window width to 760px #1126 #1129
|
||||
|
||||
## 3.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [8a7eae4]
|
||||
- react-json-tree@0.17.0
|
||||
- @redux-devtools/app@2.2.0
|
||||
- @redux-devtools/slider-monitor@4.0.0
|
||||
- @redux-devtools/ui@1.3.0
|
||||
- @redux-devtools/core@3.13.0
|
||||
- @redux-devtools/utils@2.0.0
|
||||
|
||||
## 3.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4891bf6]
|
||||
- @redux-devtools/core@3.12.0
|
||||
- @redux-devtools/slider-monitor@3.1.2
|
||||
- @redux-devtools/utils@1.2.1
|
||||
- @redux-devtools/app@2.1.4
|
||||
|
||||
## 3.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ab3c0e2: Avoid persisting the selected action index between sessions
|
||||
- Updated dependencies [ab3c0e2]
|
||||
- Updated dependencies [4c9a890]
|
||||
- @redux-devtools/app@2.1.3
|
||||
- react-json-tree@0.16.2
|
||||
|
||||
## 3.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 55cc37e: Fix filter to show state-controlled search value
|
||||
- Updated dependencies [55cc37e]
|
||||
- @redux-devtools/app@2.1.2
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/zalmoxisus/redux-devtools-extension/releases) page.
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
- from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd);
|
||||
- or download `extension.zip` from [last releases](https://github.com/zalmoxisus/redux-devtools-extension/releases), unzip, open `chrome://extensions` url and turn on developer mode from top left and then click; on `Load Unpacked` and select the extracted folder for use
|
||||
- or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked) `./build/extension`;
|
||||
- or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked) `./dev`.
|
||||
- or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./build/extension`;
|
||||
- or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./dev`.
|
||||
|
||||
### 2. For Firefox
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ const composeEnhancers =
|
|||
compose;
|
||||
```
|
||||
|
||||
> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtoolsextension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](docs/Recipes.md#using-in-a-typescript-project) for an example).
|
||||
> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtools-extension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](/docs/Recipes.md#using-in-a-typescript-project) for an example).
|
||||
|
||||
```js
|
||||
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
|
@ -100,12 +100,12 @@ To specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-
|
|||
const composeEnhancers =
|
||||
typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
|
||||
// Specify extension’s options like name, actionsDenylist, actionsCreators, serialize...
|
||||
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
|
||||
})
|
||||
: compose;
|
||||
|
||||
const enhancer = composeEnhancers(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
);
|
||||
const store = createStore(reducer, enhancer);
|
||||
|
|
@ -113,26 +113,26 @@ const store = createStore(reducer, enhancer);
|
|||
|
||||
> [See the post for more details](https://medium.com/@zalmoxis/improve-your-development-workflow-with-redux-devtools-extension-f0379227ff83).
|
||||
|
||||
### 1.3 Use `@redux-devtools/extension` package from npm
|
||||
### 1.3 Use `redux-devtools-extension` package from npm
|
||||
|
||||
To make things easier, there's an npm package to install:
|
||||
|
||||
```
|
||||
npm install --save @redux-devtools/extension
|
||||
npm install --save redux-devtools-extension
|
||||
```
|
||||
|
||||
and to use like so:
|
||||
|
||||
```js
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
composeWithDevTools(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -140,32 +140,32 @@ To specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-
|
|||
|
||||
```js
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
|
||||
const composeEnhancers = composeWithDevTools({
|
||||
// Specify name here, actionsDenylist, actionsCreators and other options if needed
|
||||
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
|
||||
});
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
> There are just a [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js) added to your bundle.
|
||||
> There’re just [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js) added to your bundle.
|
||||
|
||||
In case you don't include other enhancers and middlewares, just use `devToolsEnhancer`:
|
||||
|
||||
```js
|
||||
import { createStore } from 'redux';
|
||||
import { devToolsEnhancer } from '@redux-devtools/extension';
|
||||
import { devToolsEnhancer } from 'redux-devtools-extension';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ devToolsEnhancer(),
|
||||
// Specify name here, actionsDenylist, actionsCreators and other options if needed
|
||||
/* preloadedState, */ devToolsEnhancer()
|
||||
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -173,15 +173,15 @@ const store = createStore(
|
|||
|
||||
It's useful to include the extension in production as well. Usually you [can use it for development](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f).
|
||||
|
||||
If you want to restrict it there, use `composeWithDevToolsLogOnlyInProduction` or `devToolsEnhancerLogOnlyInProduction`:
|
||||
If you want to restrict it there, use `redux-devtools-extension/logOnlyInProduction`:
|
||||
|
||||
```js
|
||||
import { createStore } from 'redux';
|
||||
import { devToolsEnhancerLogOnlyInProduction } from '@redux-devtools/extension';
|
||||
import { devToolsEnhancer } from 'redux-devtools-extension/logOnlyInProduction';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ devToolsEnhancerLogOnlyInProduction(),
|
||||
/* preloadedState, */ devToolsEnhancer()
|
||||
// options like actionSanitizer, stateSanitizer
|
||||
);
|
||||
```
|
||||
|
|
@ -190,25 +190,25 @@ or with middlewares and enhancers:
|
|||
|
||||
```js
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { composeWithDevToolsLogOnlyInProduction } from '@redux-devtools/extension';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction';
|
||||
|
||||
const composeEnhancers = composeWithDevToolsLogOnlyInProduction({
|
||||
const composeEnhancers = composeWithDevTools({
|
||||
// options like actionSanitizer, stateSanitizer
|
||||
});
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
> You'll have to add `'process.env.NODE_ENV': JSON.stringify('production')` in your Webpack config for the production bundle ([to envify](https://github.com/gaearon/redux-devtools/blob/master/docs/Walkthrough.md#exclude-devtools-from-production-builds)). If you use `create-react-app`, [it already does it for you.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/webpack.config.prod.js#L253-L257)
|
||||
|
||||
If you're already checking `process.env.NODE_ENV` when creating the store, import `composeWithDevToolsLogOnly` or `devToolsEnhancerLogOnly` for production environment.
|
||||
If you're already checking `process.env.NODE_ENV` when creating the store, include `redux-devtools-extension/logOnly` for production environment.
|
||||
|
||||
If you don’t want to allow the extension in production, just use `composeWithDevToolsDevelopmentOnly` or `devToolsEnhancerDevelopmentOnly`.
|
||||
If you don’t want to allow the extension in production, just use `redux-devtools-extension/developmentOnly`.
|
||||
|
||||
> See [the article](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f) for more details.
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ See [integrations](docs/Integrations.md) and [the blog post](https://medium.com/
|
|||
- [Methods (advanced API)](docs/API/Methods.md)
|
||||
- [FAQ](docs/FAQ.md)
|
||||
- Features
|
||||
- [Trace actions calls](docs/Features/Trace.md)
|
||||
- [Trace actions calls](/docs/Features/Trace.md)
|
||||
- [Troubleshooting](docs/Troubleshooting.md)
|
||||
- [Articles](docs/Articles.md)
|
||||
- [Videos](docs/Videos.md)
|
||||
|
|
|
|||
1
extension/SUMMARY.md
Normal file
1
extension/SUMMARY.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
./docs/README.md
|
||||
18
extension/appveyor.yml
Normal file
18
extension/appveyor.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
environment:
|
||||
matrix:
|
||||
- nodejs_version: '6'
|
||||
|
||||
cache:
|
||||
- '%LOCALAPPDATA%/Yarn'
|
||||
- node_modules
|
||||
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- yarn install
|
||||
|
||||
test_script:
|
||||
- node --version
|
||||
- yarn --version
|
||||
- yarn test
|
||||
|
||||
build: off
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", { "targets": "defaults" }],
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-typescript"
|
||||
]
|
||||
}
|
||||
18
extension/book.json
Normal file
18
extension/book.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"gitbook": "3.2.2",
|
||||
"title": "Redux DevTools Extension",
|
||||
"plugins": ["edit-link", "prism", "-highlight", "github", "anchorjs"],
|
||||
"pluginsConfig": {
|
||||
"edit-link": {
|
||||
"base": "https://github.com/zalmoxisus/redux-devtools-extension/tree/master",
|
||||
"label": "Edit This Page"
|
||||
},
|
||||
"github": {
|
||||
"url": "https://github.com/zalmoxisus/redux-devtools-extension/"
|
||||
},
|
||||
"sharing": {
|
||||
"facebook": true,
|
||||
"twitter": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import * as fs from 'node:fs';
|
||||
import * as esbuild from 'esbuild';
|
||||
import pug from 'pug';
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const prod = !args.includes('--dev');
|
||||
|
||||
await esbuild.build({
|
||||
bundle: true,
|
||||
logLevel: 'info',
|
||||
outdir: 'dist',
|
||||
minify: prod,
|
||||
sourcemap: !prod,
|
||||
define: {
|
||||
'process.env.NODE_ENV': prod ? '"production"' : '"development"',
|
||||
'process.env.BABEL_ENV': prod ? '"production"' : '"development"',
|
||||
},
|
||||
entryPoints: [
|
||||
{ out: 'background.bundle', in: 'src/background/index.ts' },
|
||||
{ out: 'options.bundle', in: 'src/options/index.tsx' },
|
||||
{ out: 'remote.bundle', in: 'src/remote/index.tsx' },
|
||||
{ out: 'devpanel.bundle', in: 'src/devpanel/index.tsx' },
|
||||
{ out: 'devtools.bundle', in: 'src/devtools/index.ts' },
|
||||
{ out: 'content.bundle', in: 'src/contentScript/index.ts' },
|
||||
{ out: 'page.bundle', in: 'src/pageScript/index.ts' },
|
||||
],
|
||||
loader: {
|
||||
'.woff2': 'file',
|
||||
},
|
||||
});
|
||||
|
||||
console.log();
|
||||
|
||||
console.log('Creating HTML files...');
|
||||
const htmlFiles = ['devpanel', 'devtools', 'options', 'remote'];
|
||||
for (const htmlFile of htmlFiles) {
|
||||
fs.writeFileSync(
|
||||
`dist/${htmlFile}.html`,
|
||||
pug.renderFile(`src/${htmlFile}/${htmlFile}.pug`),
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Copying manifest.json...');
|
||||
fs.copyFileSync('chrome/manifest.json', 'dist/manifest.json');
|
||||
|
||||
console.log('Copying assets...');
|
||||
fs.cpSync('src/assets', 'dist', { recursive: true });
|
||||
|
||||
console.log('Copying dist for each browser...');
|
||||
fs.cpSync('dist', 'chrome/dist', { recursive: true });
|
||||
fs.copyFileSync('chrome/manifest.json', 'chrome/dist/manifest.json');
|
||||
fs.cpSync('dist', 'edge/dist', { recursive: true });
|
||||
fs.copyFileSync('edge/manifest.json', 'edge/dist/manifest.json');
|
||||
fs.cpSync('dist', 'firefox/dist', { recursive: true });
|
||||
fs.copyFileSync('firefox/manifest.json', 'firefox/dist/manifest.json');
|
||||
|
|
@ -5,18 +5,18 @@ Use with
|
|||
- `window.__REDUX_DEVTOOLS_EXTENSION__([options])`
|
||||
- `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__([options])()`
|
||||
- `window.__REDUX_DEVTOOLS_EXTENSION__.connect([options])`
|
||||
- `@redux-devtools/extension` npm package:
|
||||
- `redux-devtools-extension` npm package:
|
||||
|
||||
```js
|
||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
|
||||
const composeEnhancers = composeWithDevTools(options);
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -51,6 +51,7 @@ _number_ - maximum stack trace frames to be stored (in case `trace` option was p
|
|||
_boolean_ or _object_ which contains:
|
||||
|
||||
- **options** `object or boolean`:
|
||||
|
||||
- `undefined` - will use regular `JSON.stringify` to send data (it's the fast mode).
|
||||
- `false` - will handle also circular references.
|
||||
- `true` - will handle also date, regex, undefined, primitives, error objects, symbols, maps, sets and functions.
|
||||
|
|
@ -69,7 +70,7 @@ _boolean_ or _object_ which contains:
|
|||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -86,7 +87,7 @@ _boolean_ or _object_ which contains:
|
|||
replacer: (key, value) =>
|
||||
value && mori.isMap(value) ? mori.toJs(value) : value,
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -108,7 +109,7 @@ _boolean_ or _object_ which contains:
|
|||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -133,7 +134,7 @@ _boolean_ or _object_ which contains:
|
|||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -173,7 +174,7 @@ _boolean_ or _object_ which contains:
|
|||
immutable: Immutable,
|
||||
refs: [ABRecord],
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -184,7 +185,7 @@ In the example bellow it will always send `{ component: '[React]' }`, regardless
|
|||
```js
|
||||
function component(
|
||||
state = { component: null, toJSON: () => ({ component: '[React]' }) },
|
||||
action,
|
||||
action
|
||||
) {
|
||||
switch (action.type) {
|
||||
case 'ADD_COMPONENT':
|
||||
|
|
@ -205,7 +206,7 @@ function counter(
|
|||
return { conter: this.count * 10 };
|
||||
},
|
||||
},
|
||||
action,
|
||||
action
|
||||
) {
|
||||
switch (action.type) {
|
||||
case 'INCREMENT':
|
||||
|
|
@ -235,13 +236,13 @@ const store = createStore(
|
|||
actionSanitizer,
|
||||
stateSanitizer: (state) =>
|
||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### `actionsDenylist` / `actionsAllowlist`
|
||||
### `actionsBlacklist` / `actionsWhitelist`
|
||||
|
||||
_string or array of strings as regex_ - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsAllowlist` specified, `actionsDenylist` is ignored.
|
||||
_string or array of strings as regex_ - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsWhitelist` specified, `actionsBlacklist` is ignored.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -250,16 +251,16 @@ createStore(
|
|||
reducer,
|
||||
remotedev({
|
||||
sendTo: 'http://localhost:8000',
|
||||
actionsDenylist: 'SOME_ACTION',
|
||||
// or actionsDenylist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
|
||||
// or just actionsDenylist: 'SOME_' to omit both
|
||||
}),
|
||||
actionsBlacklist: 'SOME_ACTION',
|
||||
// or actionsBlacklist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
|
||||
// or just actionsBlacklist: 'SOME_' to omit both
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### `predicate`
|
||||
|
||||
_function_ - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsDenylist`/`actionsAllowlist` parameters.
|
||||
_function_ - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters.
|
||||
Example of usage:
|
||||
|
||||
```js
|
||||
|
|
@ -269,7 +270,7 @@ const store = createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||
predicate: (state, action) =>
|
||||
state.dev.logLevel === VERBOSE && !action.forwarded,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ devTools.init({ value: 'initial state' });
|
|||
devTools.send('change state', { value: 'state changed' });
|
||||
```
|
||||
|
||||
See [redux enhancer's example](https://github.com/reduxjs/redux-devtools/blob/main/packages/redux-devtools-extension/src/logOnly.ts), [react example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/react-counter-messaging/components/Counter.js) and [blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details.
|
||||
See [redux enhancer's example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), [react example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/react-counter-messaging/components/Counter.js) and [blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details.
|
||||
|
||||
### disconnect()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
# Architecture Notes
|
||||
|
||||
This document exists to keep track of how the different parts of the Redux DevTools interact, since it's easy to forget how it all works together. This is intended for internal purposes and is just a collection of notes to myself.
|
||||
|
||||
## Entry Points
|
||||
|
||||
### Window
|
||||
|
||||
This is the default view that is shown in the Redux DevTools popup, the Chrome DevTools tab (if direct access to the background page is available), and new popup windows that are created. It has direct access to the background page via `chrome.runtime.getBackgroundPage`.
|
||||
|
||||
### DevPanel
|
||||
|
||||
This is the view that is shown in the Chrome DevTools tab if direct access to the background page is not available.
|
||||
|
||||
Initially this was the view that was always used for the Chrome DevTools tab, but when support to directly access the background page from the DevTools tab was added, [the Window View became the preferred view](https://github.com/zalmoxisus/redux-devtools-extension/pull/580).
|
||||
|
||||
### Remote
|
||||
|
||||
This does not interact with the other parts of the extension at all, it just renders the `App` component from `@redux-devtools/app`.
|
||||
|
||||
It can be triggered by hitting the "Remote" button in any of the other views, which calls `chrome.windows.create` and creates a new window.
|
||||
|
||||
### DevTools
|
||||
|
||||
This is the script that adds the Redux panel in the Chrome DevTools using `chrome.devtools.panels.create`.
|
||||
|
||||
It creates a Window View if it has direct access to the background page, otherwise it creates a DevPanel View.
|
||||
|
||||
Note that this used to always show the DevPanel View, but [started using the Window View by default](https://github.com/zalmoxisus/redux-devtools-extension/pull/580) once direct access to the background page was added to Chrome DevTools tabs.
|
||||
|
||||
### Content Script
|
||||
|
||||
Passes messages between the injected page script and the background page.
|
||||
|
||||
It listens for messages from the injected page script using `window.addEventListener('message', ...)`. It knows the message is from the injected page script if `message.source` is `'@devtools-page'`. See the Chrome DevTools docs where this approach [is documented](https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools#evaluated-scripts-to-devtools).
|
||||
|
||||
It creates a connection to the background page using `chrome.runtime.connect` with the name `'tab'` when it receives the first message from the injected page script.
|
||||
|
|
@ -32,7 +32,7 @@ import { inspectProps } from 'react-inspect-props';
|
|||
|
||||
compose(
|
||||
withState('count', 'setCount', 0),
|
||||
inspectProps('Counter inspector'),
|
||||
inspectProps('Counter inspector')
|
||||
)(Counter);
|
||||
```
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ run(App, {
|
|||
{ id: newId(), num: 0 },
|
||||
{ id: newId(), num: 0 },
|
||||
],
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
### Using in a typescript project
|
||||
|
||||
The recommended way is to use [`@redux-devtools/extension` npm package](/README.md#13-use-redux-devtools-extension-package-from-npm), which contains all typescript definitions. Or you can just use `window as any`:
|
||||
The recommended way is to use [`redux-devtools-extension` npm package](/README.md#13-use-redux-devtools-extension-package-from-npm), which contains all typescript definitions. Or you can just use `window as any`:
|
||||
|
||||
```js
|
||||
const store = createStore(
|
||||
|
|
@ -15,7 +15,7 @@ const store = createStore(
|
|||
|
||||
Note that you many need to set `no-any` to false in your `tslint.json` file.
|
||||
|
||||
Alternatively you can use type-guard in order to avoid
|
||||
Alternatively you can use typeguard in order to avoid
|
||||
casting to any.
|
||||
|
||||
```typescript
|
||||
|
|
@ -28,7 +28,7 @@ type WindowWithDevTools = Window & {
|
|||
};
|
||||
|
||||
const isReduxDevtoolsExtenstionExist = (
|
||||
arg: Window | WindowWithDevTools,
|
||||
arg: Window | WindowWithDevTools
|
||||
): arg is WindowWithDevTools => {
|
||||
return '__REDUX_DEVTOOLS_EXTENSION__' in arg;
|
||||
};
|
||||
|
|
@ -40,7 +40,7 @@ const store = createStore(
|
|||
initialState,
|
||||
isReduxDevtoolsExtenstionExist(window)
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||
: undefined,
|
||||
: undefined
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -54,25 +54,25 @@ The extension is not sharing `store` object, so you should take care of that.
|
|||
|
||||
### Applying multiple times with different sets of options
|
||||
|
||||
We're [not allowing that from instrumentation part](https://github.com/reduxjs/redux-devtools/blob/main/packages/redux-devtools-extension/src/logOnly.ts), which can be used it like so:
|
||||
We're [not allowing that from instrumentation part](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js#L676), because that would re-dispatch every app action in case we'd have many liftedStores, but there's [a helper for logging only](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), which can be used it like so:
|
||||
|
||||
```js
|
||||
import { createStore, compose } from 'redux';
|
||||
import { devToolsEnhancerLogOnly } from '@redux-devtools/extension';
|
||||
import { devToolsEnhancer } from 'redux-devtools-extension/logOnly';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ compose(
|
||||
devToolsEnhancerLogOnly({
|
||||
devToolsEnhancer({
|
||||
instaceID: 1,
|
||||
name: 'Denylisted',
|
||||
actionsDenylist: '...',
|
||||
name: 'Blacklisted',
|
||||
actionsBlacklist: '...',
|
||||
}),
|
||||
devToolsEnhancerLogOnly({
|
||||
devToolsEnhancer({
|
||||
instaceID: 2,
|
||||
name: 'Allowlisted',
|
||||
actionsAllowlist: '...',
|
||||
}),
|
||||
),
|
||||
name: 'Whitelisted',
|
||||
actionsWhitelist: '...',
|
||||
})
|
||||
)
|
||||
);
|
||||
```
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ If you develop on your local filesystem, make sure to allow Redux DevTools acces
|
|||
|
||||
Most likely you mutate the state. Check it by [adding `redux-immutable-state-invariant` middleware](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/counter/store/configureStore.js#L3).
|
||||
|
||||
Another cause could be that you are creating multiple stores, which means that the devtools get attached to one but the application uses another. See [https://github.com/reduxjs/redux-toolkit/issues/2753](this issue).
|
||||
|
||||
### @@INIT or REPLACE action resets the state of the app or last actions RE-APPLIED
|
||||
|
||||
`@@redux/REPLACE` (or `@@INIT`) is used internally when the application is hot reloaded. When you use `store.replaceReducer` the effect will be the same as for hot-reloading, where the extension is recomputing all the history again. To avoid that set [`shouldHotReload`](/docs/API/Arguments.md#shouldhotreload) parameter to `false`.
|
||||
|
|
@ -35,8 +33,8 @@ const store = createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||
: (noop) => noop,
|
||||
batchedSubscribe(/* ... */),
|
||||
),
|
||||
batchedSubscribe(/* ... */)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -60,7 +58,7 @@ const store = createStore(
|
|||
actionSanitizer,
|
||||
stateSanitizer: (state) =>
|
||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -124,7 +122,7 @@ const store = Redux.createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__ &&
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||
serialize: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
{
|
||||
"version": "3.2.10",
|
||||
"name": "Redux DevTools",
|
||||
"description": "Redux DevTools for debugging application's state changes.",
|
||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||
"manifest_version": 3,
|
||||
"action": {
|
||||
"default_icon": "img/logo/gray.png",
|
||||
"default_title": "Redux DevTools",
|
||||
"default_popup": "devpanel.html#popup"
|
||||
},
|
||||
"commands": {
|
||||
"devtools-window": {
|
||||
"description": "DevTools window"
|
||||
},
|
||||
"devtools-remote": {
|
||||
"description": "Remote DevTools"
|
||||
},
|
||||
"_execute_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+E"
|
||||
}
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "img/logo/16x16.png",
|
||||
"48": "img/logo/48x48.png",
|
||||
"128": "img/logo/128x128.png"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.bundle.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"exclude_globs": ["https://www.google*"],
|
||||
"js": ["content.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"exclude_globs": ["https://www.google*"],
|
||||
"js": ["page.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"world": "MAIN"
|
||||
}
|
||||
],
|
||||
"devtools_page": "devtools.html",
|
||||
"externally_connectable": {
|
||||
"ids": ["*"]
|
||||
},
|
||||
"permissions": ["notifications", "contextMenus", "storage"],
|
||||
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import globals from 'globals';
|
||||
import eslintJs from '../eslint.js.config.base.mjs';
|
||||
import eslintTsReact from '../eslint.ts.react.config.base.mjs';
|
||||
import eslintJsReactJest from '../eslint.js.react.jest.config.base.mjs';
|
||||
|
||||
export default [
|
||||
...eslintJs,
|
||||
...eslintTsReact(import.meta.dirname),
|
||||
...eslintJsReactJest,
|
||||
{
|
||||
ignores: [
|
||||
'chrome',
|
||||
'dist',
|
||||
'edge',
|
||||
'examples',
|
||||
'firefox',
|
||||
'jest.config.ts',
|
||||
'test/electron/fixture/dist',
|
||||
],
|
||||
},
|
||||
{
|
||||
files: ['build.mjs'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
EUI: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
36
extension/examples/buildAll.js
Normal file
36
extension/examples/buildAll.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Runs an ordered set of commands within each of the build directories.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { spawnSync } from 'child_process';
|
||||
|
||||
var exampleDirs = fs.readdirSync(__dirname).filter((file) => {
|
||||
return fs.statSync(path.join(__dirname, file)).isDirectory();
|
||||
});
|
||||
|
||||
// Ordering is important here. `npm install` must come first.
|
||||
var cmdArgs = [
|
||||
{ cmd: 'npm', args: ['install'] },
|
||||
{ cmd: 'webpack', args: ['index.js'] },
|
||||
];
|
||||
|
||||
for (const dir of exampleDirs) {
|
||||
for (const cmdArg of cmdArgs) {
|
||||
// declare opts in this scope to avoid https://github.com/joyent/node/issues/9158
|
||||
const opts = {
|
||||
cwd: path.join(__dirname, dir),
|
||||
stdio: 'inherit',
|
||||
};
|
||||
let result = {};
|
||||
if (process.platform === 'win32') {
|
||||
result = spawnSync(cmdArg.cmd + '.cmd', cmdArg.args, opts);
|
||||
} else {
|
||||
result = spawnSync(cmdArg.cmd, cmdArg.args, opts);
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
throw new Error('Building examples exited with non-zero');
|
||||
}
|
||||
}
|
||||
}
|
||||
3
extension/examples/counter/.babelrc
Normal file
3
extension/examples/counter/.babelrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["es2015", "stage-0", "react"]
|
||||
}
|
||||
37
extension/examples/counter/actions/counter.js
Normal file
37
extension/examples/counter/actions/counter.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
|
||||
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
|
||||
|
||||
let t;
|
||||
|
||||
export function increment() {
|
||||
return {
|
||||
type: INCREMENT_COUNTER,
|
||||
};
|
||||
}
|
||||
|
||||
export function decrement() {
|
||||
return {
|
||||
type: DECREMENT_COUNTER,
|
||||
};
|
||||
}
|
||||
|
||||
export function autoIncrement(delay = 10) {
|
||||
return (dispatch) => {
|
||||
if (t) {
|
||||
clearInterval(t);
|
||||
t = undefined;
|
||||
return;
|
||||
}
|
||||
t = setInterval(() => {
|
||||
dispatch(increment());
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
|
||||
export function incrementAsync(delay = 1000) {
|
||||
return (dispatch) => {
|
||||
setTimeout(() => {
|
||||
dispatch(increment());
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
32
extension/examples/counter/components/Counter.js
Normal file
32
extension/examples/counter/components/Counter.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class Counter extends Component {
|
||||
render() {
|
||||
const {
|
||||
increment,
|
||||
autoIncrement,
|
||||
incrementAsync,
|
||||
decrement,
|
||||
counter,
|
||||
} = this.props;
|
||||
return (
|
||||
<p>
|
||||
Clicked: {counter} times <button onClick={increment}>+</button>{' '}
|
||||
<button onClick={decrement}>-</button>{' '}
|
||||
<button onClick={incrementAsync}>Increment async</button>{' '}
|
||||
<button onClick={autoIncrement}>Auto increment</button>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Counter.propTypes = {
|
||||
increment: PropTypes.func.isRequired,
|
||||
autoIncrement: PropTypes.func.isRequired,
|
||||
incrementAsync: PropTypes.func.isRequired,
|
||||
decrement: PropTypes.func.isRequired,
|
||||
counter: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
export default Counter;
|
||||
16
extension/examples/counter/containers/App.js
Normal file
16
extension/examples/counter/containers/App.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import Counter from '../components/Counter';
|
||||
import * as CounterActions from '../actions/counter';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
counter: state.counter,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(CounterActions, dispatch);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
|
||||
10
extension/examples/counter/index.html
Normal file
10
extension/examples/counter/index.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redux counter example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="/static/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
14
extension/examples/counter/index.js
Normal file
14
extension/examples/counter/index.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import App from './containers/App';
|
||||
import configureStore from './store/configureStore';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
41
extension/examples/counter/package.json
Normal file
41
extension/examples/counter/package.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "redux-counter-example",
|
||||
"version": "0.0.0",
|
||||
"description": "Redux counter example",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"test": "NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js",
|
||||
"test:watch": "npm test -- --watch"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rackt/redux.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rackt/redux/issues"
|
||||
},
|
||||
"homepage": "http://rackt.github.io/redux",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-redux": "^6.0.0",
|
||||
"redux": "^4.0.1",
|
||||
"redux-devtools-extension": "^2.13.7",
|
||||
"redux-thunk": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.3.17",
|
||||
"babel-core": "^6.3.17",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-preset-es2015": "^6.0.0",
|
||||
"babel-preset-react": "6.3.13",
|
||||
"babel-preset-stage-0": "^6.3.13",
|
||||
"express": "^4.13.3",
|
||||
"redux-immutable-state-invariant": "^2.1.0",
|
||||
"webpack": "^4.0.0",
|
||||
"webpack-dev-server": "^3.0.0",
|
||||
"webpack-hot-middleware": "^2.2.0"
|
||||
}
|
||||
}
|
||||
12
extension/examples/counter/reducers/counter.js
Normal file
12
extension/examples/counter/reducers/counter.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';
|
||||
|
||||
export default function counter(state = 0, action) {
|
||||
switch (action.type) {
|
||||
case INCREMENT_COUNTER:
|
||||
return state + 1;
|
||||
case DECREMENT_COUNTER:
|
||||
return state - 1;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
8
extension/examples/counter/reducers/index.js
Normal file
8
extension/examples/counter/reducers/index.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import counter from './counter';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
counter,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
||||
32
extension/examples/counter/server.js
Normal file
32
extension/examples/counter/server.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
var webpack = require('webpack');
|
||||
var webpackDevMiddleware = require('webpack-dev-middleware');
|
||||
var webpackHotMiddleware = require('webpack-hot-middleware');
|
||||
var config = require('./webpack.config');
|
||||
|
||||
var app = new require('express')();
|
||||
var port = 4001;
|
||||
|
||||
var compiler = webpack(config);
|
||||
app.use(
|
||||
webpackDevMiddleware(compiler, {
|
||||
noInfo: true,
|
||||
publicPath: config.output.publicPath,
|
||||
})
|
||||
);
|
||||
app.use(webpackHotMiddleware(compiler));
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
res.sendFile(__dirname + '/index.html');
|
||||
});
|
||||
|
||||
app.listen(port, function (error) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
console.info(
|
||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||
port,
|
||||
port
|
||||
);
|
||||
}
|
||||
});
|
||||
28
extension/examples/counter/store/configureStore.js
Normal file
28
extension/examples/counter/store/configureStore.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
import thunk from 'redux-thunk';
|
||||
import invariant from 'redux-immutable-state-invariant';
|
||||
import reducer from '../reducers';
|
||||
import * as actionCreators from '../actions/counter';
|
||||
|
||||
export default function configureStore(preloadedState) {
|
||||
const composeEnhancers = composeWithDevTools({
|
||||
actionCreators,
|
||||
trace: true,
|
||||
traceLimit: 25,
|
||||
});
|
||||
const store = createStore(
|
||||
reducer,
|
||||
preloadedState,
|
||||
composeEnhancers(applyMiddleware(invariant(), thunk))
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
// Enable Webpack hot module replacement for reducers
|
||||
module.hot.accept('../reducers', () => {
|
||||
store.replaceReducer(require('../reducers').default);
|
||||
});
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
73
extension/examples/counter/test/actions/counter.spec.js
Normal file
73
extension/examples/counter/test/actions/counter.spec.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import expect from 'expect';
|
||||
import { applyMiddleware } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import * as actions from '../../actions/counter';
|
||||
|
||||
const middlewares = [thunk];
|
||||
|
||||
/*
|
||||
* Creates a mock of Redux store with middleware.
|
||||
*/
|
||||
function mockStore(getState, expectedActions, onLastAction) {
|
||||
if (!Array.isArray(expectedActions)) {
|
||||
throw new Error('expectedActions should be an array of expected actions.');
|
||||
}
|
||||
if (
|
||||
typeof onLastAction !== 'undefined' &&
|
||||
typeof onLastAction !== 'function'
|
||||
) {
|
||||
throw new Error('onLastAction should either be undefined or function.');
|
||||
}
|
||||
|
||||
function mockStoreWithoutMiddleware() {
|
||||
return {
|
||||
getState() {
|
||||
return typeof getState === 'function' ? getState() : getState;
|
||||
},
|
||||
|
||||
dispatch(action) {
|
||||
const expectedAction = expectedActions.shift();
|
||||
expect(action).toEqual(expectedAction);
|
||||
if (onLastAction && !expectedActions.length) {
|
||||
onLastAction();
|
||||
}
|
||||
return action;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const mockStoreWithMiddleware = applyMiddleware(...middlewares)(
|
||||
mockStoreWithoutMiddleware
|
||||
);
|
||||
|
||||
return mockStoreWithMiddleware();
|
||||
}
|
||||
|
||||
describe('actions', () => {
|
||||
it('increment should create increment action', () => {
|
||||
expect(actions.increment()).toEqual({ type: actions.INCREMENT_COUNTER });
|
||||
});
|
||||
|
||||
it('decrement should create decrement action', () => {
|
||||
expect(actions.decrement()).toEqual({ type: actions.DECREMENT_COUNTER });
|
||||
});
|
||||
|
||||
it('incrementIfOdd should create increment action', (done) => {
|
||||
const expectedActions = [{ type: actions.INCREMENT_COUNTER }];
|
||||
const store = mockStore({ counter: 1 }, expectedActions, done);
|
||||
store.dispatch(actions.incrementIfOdd());
|
||||
});
|
||||
|
||||
it('incrementIfOdd shouldnt create increment action if counter is even', (done) => {
|
||||
const expectedActions = [];
|
||||
const store = mockStore({ counter: 2 }, expectedActions);
|
||||
store.dispatch(actions.incrementIfOdd());
|
||||
done();
|
||||
});
|
||||
|
||||
it('incrementAsync should create increment action', (done) => {
|
||||
const expectedActions = [{ type: actions.INCREMENT_COUNTER }];
|
||||
const store = mockStore({ counter: 0 }, expectedActions, done);
|
||||
store.dispatch(actions.incrementAsync(100));
|
||||
});
|
||||
});
|
||||
53
extension/examples/counter/test/components/Counter.spec.js
Normal file
53
extension/examples/counter/test/components/Counter.spec.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import expect from 'expect';
|
||||
import React from 'react';
|
||||
import TestUtils from 'react-addons-test-utils';
|
||||
import Counter from '../../components/Counter';
|
||||
|
||||
function setup() {
|
||||
const actions = {
|
||||
increment: expect.createSpy(),
|
||||
incrementIfOdd: expect.createSpy(),
|
||||
incrementAsync: expect.createSpy(),
|
||||
decrement: expect.createSpy(),
|
||||
};
|
||||
const component = TestUtils.renderIntoDocument(
|
||||
<Counter counter={1} {...actions} />
|
||||
);
|
||||
return {
|
||||
component: component,
|
||||
actions: actions,
|
||||
buttons: TestUtils.scryRenderedDOMComponentsWithTag(component, 'button'),
|
||||
p: TestUtils.findRenderedDOMComponentWithTag(component, 'p'),
|
||||
};
|
||||
}
|
||||
|
||||
describe('Counter component', () => {
|
||||
it('should display count', () => {
|
||||
const { p } = setup();
|
||||
expect(p.textContent).toMatch(/^Clicked: 1 times/);
|
||||
});
|
||||
|
||||
it('first button should call increment', () => {
|
||||
const { buttons, actions } = setup();
|
||||
TestUtils.Simulate.click(buttons[0]);
|
||||
expect(actions.increment).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('second button should call decrement', () => {
|
||||
const { buttons, actions } = setup();
|
||||
TestUtils.Simulate.click(buttons[1]);
|
||||
expect(actions.decrement).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('third button should call incrementIfOdd', () => {
|
||||
const { buttons, actions } = setup();
|
||||
TestUtils.Simulate.click(buttons[2]);
|
||||
expect(actions.incrementIfOdd).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fourth button should call incrementAsync', () => {
|
||||
const { buttons, actions } = setup();
|
||||
TestUtils.Simulate.click(buttons[3]);
|
||||
expect(actions.incrementAsync).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
53
extension/examples/counter/test/containers/App.spec.js
Normal file
53
extension/examples/counter/test/containers/App.spec.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import expect from 'expect';
|
||||
import React from 'react';
|
||||
import TestUtils from 'react-addons-test-utils';
|
||||
import { Provider } from 'react-redux';
|
||||
import App from '../../containers/App';
|
||||
import configureStore from '../../store/configureStore';
|
||||
|
||||
function setup(initialState) {
|
||||
const store = configureStore(initialState);
|
||||
const app = TestUtils.renderIntoDocument(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
return {
|
||||
app: app,
|
||||
buttons: TestUtils.scryRenderedDOMComponentsWithTag(app, 'button'),
|
||||
p: TestUtils.findRenderedDOMComponentWithTag(app, 'p'),
|
||||
};
|
||||
}
|
||||
|
||||
describe('containers', () => {
|
||||
describe('App', () => {
|
||||
it('should display initial count', () => {
|
||||
const { p } = setup();
|
||||
expect(p.textContent).toMatch(/^Clicked: 0 times/);
|
||||
});
|
||||
|
||||
it('should display updated count after increment button click', () => {
|
||||
const { buttons, p } = setup();
|
||||
TestUtils.Simulate.click(buttons[0]);
|
||||
expect(p.textContent).toMatch(/^Clicked: 1 times/);
|
||||
});
|
||||
|
||||
it('should display updated count after decrement button click', () => {
|
||||
const { buttons, p } = setup();
|
||||
TestUtils.Simulate.click(buttons[1]);
|
||||
expect(p.textContent).toMatch(/^Clicked: -1 times/);
|
||||
});
|
||||
|
||||
it('shouldnt change if even and if odd button clicked', () => {
|
||||
const { buttons, p } = setup();
|
||||
TestUtils.Simulate.click(buttons[2]);
|
||||
expect(p.textContent).toMatch(/^Clicked: 0 times/);
|
||||
});
|
||||
|
||||
it('should change if odd and if odd button clicked', () => {
|
||||
const { buttons, p } = setup({ counter: 1 });
|
||||
TestUtils.Simulate.click(buttons[2]);
|
||||
expect(p.textContent).toMatch(/^Clicked: 2 times/);
|
||||
});
|
||||
});
|
||||
});
|
||||
23
extension/examples/counter/test/reducers/counter.spec.js
Normal file
23
extension/examples/counter/test/reducers/counter.spec.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import expect from 'expect';
|
||||
import counter from '../../reducers/counter';
|
||||
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../actions/counter';
|
||||
|
||||
describe('reducers', () => {
|
||||
describe('counter', () => {
|
||||
it('should handle initial state', () => {
|
||||
expect(counter(undefined, {})).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle INCREMENT_COUNTER', () => {
|
||||
expect(counter(1, { type: INCREMENT_COUNTER })).toBe(2);
|
||||
});
|
||||
|
||||
it('should handle DECREMENT_COUNTER', () => {
|
||||
expect(counter(1, { type: DECREMENT_COUNTER })).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle unknown action type', () => {
|
||||
expect(counter(1, { type: 'unknown' })).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
5
extension/examples/counter/test/setup.js
Normal file
5
extension/examples/counter/test/setup.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { jsdom } from 'jsdom';
|
||||
|
||||
global.document = jsdom('<!doctype html><html><body></body></html>');
|
||||
global.window = document.defaultView;
|
||||
global.navigator = global.window.navigator;
|
||||
23
extension/examples/counter/webpack.config.js
Normal file
23
extension/examples/counter/webpack.config.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
devtool: 'source-map',
|
||||
entry: ['webpack-hot-middleware/client', './index'],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/static/',
|
||||
},
|
||||
plugins: [new webpack.HotModuleReplacementPlugin()],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loaders: ['babel-loader'],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
3
extension/examples/react-counter-messaging/.babelrc
Normal file
3
extension/examples/react-counter-messaging/.babelrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["es2015", "stage-0", "react"]
|
||||
}
|
||||
62
extension/examples/react-counter-messaging/components/Counter.js
vendored
Normal file
62
extension/examples/react-counter-messaging/components/Counter.js
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
const withDevTools =
|
||||
// process.env.NODE_ENV === 'development' &&
|
||||
typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__;
|
||||
|
||||
class Counter extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { counter: 0 };
|
||||
|
||||
this.increment = this.increment.bind(this);
|
||||
this.decrement = this.decrement.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
if (withDevTools) {
|
||||
this.devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect();
|
||||
this.unsubscribe = this.devTools.subscribe((message) => {
|
||||
// Implement monitors actions.
|
||||
// For example time traveling:
|
||||
if (
|
||||
message.type === 'DISPATCH' &&
|
||||
message.payload.type === 'JUMP_TO_STATE'
|
||||
) {
|
||||
this.setState(message.state);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (withDevTools) {
|
||||
this.unsubscribe(); // Use if you have other subscribers from other components.
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__.disconnect(); // If there aren't other subscribers.
|
||||
}
|
||||
}
|
||||
|
||||
increment() {
|
||||
const state = { counter: this.state.counter + 1 };
|
||||
if (withDevTools) this.devTools.send('increment', state);
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
decrement() {
|
||||
const state = { counter: this.state.counter - 1 };
|
||||
if (withDevTools) this.devTools.send('decrement', state);
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { counter } = this.state;
|
||||
return (
|
||||
<p>
|
||||
Clicked: {counter} times <button onClick={this.increment}>+</button>{' '}
|
||||
<button onClick={this.decrement}>-</button>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Counter;
|
||||
10
extension/examples/react-counter-messaging/index.html
Normal file
10
extension/examples/react-counter-messaging/index.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>React counter example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="/static/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
5
extension/examples/react-counter-messaging/index.js
vendored
Normal file
5
extension/examples/react-counter-messaging/index.js
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import Counter from './components/Counter';
|
||||
|
||||
render(<Counter />, document.getElementById('root'));
|
||||
33
extension/examples/react-counter-messaging/package.json
Normal file
33
extension/examples/react-counter-messaging/package.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "react-counter-example",
|
||||
"version": "0.0.0",
|
||||
"description": "React counter example",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --progress"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zalmoxisus/redux-devtools-extension.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/zalmoxisus/redux-devtools-extension/issues"
|
||||
},
|
||||
"homepage": "https://github.com/zalmoxisus/redux-devtools-extension",
|
||||
"dependencies": {
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.3.17",
|
||||
"babel-core": "^6.3.17",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-preset-es2015": "^6.0.0",
|
||||
"babel-preset-react": "6.3.13",
|
||||
"babel-preset-stage-0": "^6.3.13",
|
||||
"webpack": "^4.0.0",
|
||||
"webpack-cli": "^3.2.0",
|
||||
"webpack-dev-server": "^3.0.0",
|
||||
"webpack-hot-middleware": "^2.2.0"
|
||||
}
|
||||
}
|
||||
25
extension/examples/react-counter-messaging/webpack.config.js
Normal file
25
extension/examples/react-counter-messaging/webpack.config.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
devtool: 'source-map',
|
||||
entry: ['./index'],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/static/',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loaders: ['babel-loader'],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
port: 4004,
|
||||
},
|
||||
};
|
||||
4
extension/examples/router/.babelrc
Normal file
4
extension/examples/router/.babelrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"presets": ["es2015", "stage-0", "react"],
|
||||
"plugins": ["add-module-exports", "transform-decorators-legacy"]
|
||||
}
|
||||
25
extension/examples/router/actions/todos.js
Normal file
25
extension/examples/router/actions/todos.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import * as types from '../constants/ActionTypes';
|
||||
|
||||
export function addTodo(text) {
|
||||
return { type: types.ADD_TODO, text };
|
||||
}
|
||||
|
||||
export function deleteTodo(id) {
|
||||
return { type: types.DELETE_TODO, id };
|
||||
}
|
||||
|
||||
export function editTodo(id, text) {
|
||||
return { type: types.EDIT_TODO, id, text };
|
||||
}
|
||||
|
||||
export function completeTodo(id) {
|
||||
return { type: types.COMPLETE_TODO, id };
|
||||
}
|
||||
|
||||
export function completeAll() {
|
||||
return { type: types.COMPLETE_ALL };
|
||||
}
|
||||
|
||||
export function clearCompleted() {
|
||||
return { type: types.CLEAR_COMPLETED };
|
||||
}
|
||||
76
extension/examples/router/components/Footer.js
Normal file
76
extension/examples/router/components/Footer.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import {
|
||||
SHOW_ALL,
|
||||
SHOW_COMPLETED,
|
||||
SHOW_ACTIVE,
|
||||
} from '../constants/TodoFilters';
|
||||
|
||||
const FILTER_TITLES = {
|
||||
[SHOW_ALL]: 'All',
|
||||
[SHOW_ACTIVE]: 'Active',
|
||||
[SHOW_COMPLETED]: 'Completed',
|
||||
};
|
||||
|
||||
class Footer extends Component {
|
||||
renderTodoCount() {
|
||||
const { activeCount } = this.props;
|
||||
const itemWord = activeCount === 1 ? 'item' : 'items';
|
||||
|
||||
return (
|
||||
<span className="todo-count">
|
||||
<strong>{activeCount || 'No'}</strong> {itemWord} left
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
renderFilterLink(filter) {
|
||||
const title = FILTER_TITLES[filter];
|
||||
const { filter: selectedFilter, onShow } = this.props;
|
||||
|
||||
return (
|
||||
<a
|
||||
className={classnames({ selected: filter === selectedFilter })}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => onShow(filter)}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
renderClearButton() {
|
||||
const { completedCount, onClearCompleted } = this.props;
|
||||
if (completedCount > 0) {
|
||||
return (
|
||||
<button className="clear-completed" onClick={onClearCompleted}>
|
||||
Clear completed
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<footer className="footer">
|
||||
{this.renderTodoCount()}
|
||||
<ul className="filters">
|
||||
{[SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED].map((filter) => (
|
||||
<li key={filter}>{this.renderFilterLink(filter)}</li>
|
||||
))}
|
||||
</ul>
|
||||
{this.renderClearButton()}
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Footer.propTypes = {
|
||||
completedCount: PropTypes.number.isRequired,
|
||||
activeCount: PropTypes.number.isRequired,
|
||||
filter: PropTypes.string.isRequired,
|
||||
onClearCompleted: PropTypes.func.isRequired,
|
||||
onShow: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
30
extension/examples/router/components/Header.js
Normal file
30
extension/examples/router/components/Header.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import TodoTextInput from './TodoTextInput';
|
||||
|
||||
class Header extends Component {
|
||||
handleSave(text) {
|
||||
if (text.length !== 0) {
|
||||
this.props.addTodo(text);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { path } = this.props;
|
||||
return (
|
||||
<header className="header">
|
||||
<h1 style={{ fontSize: 80 }}>{path}</h1>
|
||||
<TodoTextInput
|
||||
newTodo
|
||||
onSave={this.handleSave.bind(this)}
|
||||
placeholder="What needs to be done?"
|
||||
/>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
addTodo: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Header;
|
||||
94
extension/examples/router/components/MainSection.js
Normal file
94
extension/examples/router/components/MainSection.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import TodoItem from './TodoItem';
|
||||
import Footer from './Footer';
|
||||
import {
|
||||
SHOW_ALL,
|
||||
SHOW_COMPLETED,
|
||||
SHOW_ACTIVE,
|
||||
} from '../constants/TodoFilters';
|
||||
|
||||
const TODO_FILTERS = {
|
||||
[SHOW_ALL]: () => true,
|
||||
[SHOW_ACTIVE]: (todo) => !todo.completed,
|
||||
[SHOW_COMPLETED]: (todo) => todo.completed,
|
||||
};
|
||||
|
||||
class MainSection extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = { filter: SHOW_ALL };
|
||||
}
|
||||
|
||||
handleClearCompleted() {
|
||||
const atLeastOneCompleted = this.props.todos.some((todo) => todo.completed);
|
||||
if (atLeastOneCompleted) {
|
||||
this.props.actions.clearCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
handleShow(filter) {
|
||||
this.setState({ filter });
|
||||
}
|
||||
|
||||
renderToggleAll(completedCount) {
|
||||
const { todos, actions } = this.props;
|
||||
if (todos.length > 0) {
|
||||
return (
|
||||
<input
|
||||
className="toggle-all"
|
||||
type="checkbox"
|
||||
checked={completedCount === todos.length}
|
||||
onChange={actions.completeAll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderFooter(completedCount) {
|
||||
const { todos } = this.props;
|
||||
const { filter } = this.state;
|
||||
const activeCount = todos.length - completedCount;
|
||||
|
||||
if (todos.length) {
|
||||
return (
|
||||
<Footer
|
||||
completedCount={completedCount}
|
||||
activeCount={activeCount}
|
||||
filter={filter}
|
||||
onClearCompleted={this.handleClearCompleted.bind(this)}
|
||||
onShow={this.handleShow.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { todos, actions } = this.props;
|
||||
const { filter } = this.state;
|
||||
|
||||
const filteredTodos = todos.filter(TODO_FILTERS[filter]);
|
||||
const completedCount = todos.reduce(
|
||||
(count, todo) => (todo.completed ? count + 1 : count),
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="main">
|
||||
{this.renderToggleAll(completedCount)}
|
||||
<ul className="todo-list">
|
||||
{filteredTodos.map((todo) => (
|
||||
<TodoItem key={todo.id} todo={todo} {...actions} />
|
||||
))}
|
||||
</ul>
|
||||
{this.renderFooter(completedCount)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MainSection.propTypes = {
|
||||
todos: PropTypes.array.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default MainSection;
|
||||
75
extension/examples/router/components/TodoItem.js
Normal file
75
extension/examples/router/components/TodoItem.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import TodoTextInput from './TodoTextInput';
|
||||
|
||||
class TodoItem extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleDoubleClick() {
|
||||
this.setState({ editing: true });
|
||||
}
|
||||
|
||||
handleSave(id, text) {
|
||||
if (text.length === 0) {
|
||||
this.props.deleteTodo(id);
|
||||
} else {
|
||||
this.props.editTodo(id, text);
|
||||
}
|
||||
this.setState({ editing: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { todo, completeTodo, deleteTodo } = this.props;
|
||||
|
||||
let element;
|
||||
if (this.state.editing) {
|
||||
element = (
|
||||
<TodoTextInput
|
||||
text={todo.text}
|
||||
editing={this.state.editing}
|
||||
onSave={(text) => this.handleSave(todo.id, text)}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
element = (
|
||||
<div className="view">
|
||||
<input
|
||||
className="toggle"
|
||||
type="checkbox"
|
||||
checked={todo.completed}
|
||||
onChange={() => completeTodo(todo.id)}
|
||||
/>
|
||||
<label onDoubleClick={this.handleDoubleClick.bind(this)}>
|
||||
{todo.text}
|
||||
</label>
|
||||
<button className="destroy" onClick={() => deleteTodo(todo.id)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
className={classnames({
|
||||
completed: todo.completed,
|
||||
editing: this.state.editing,
|
||||
})}
|
||||
>
|
||||
{element}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TodoItem.propTypes = {
|
||||
todo: PropTypes.object.isRequired,
|
||||
editTodo: PropTypes.func.isRequired,
|
||||
deleteTodo: PropTypes.func.isRequired,
|
||||
completeTodo: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default TodoItem;
|
||||
59
extension/examples/router/components/TodoTextInput.js
Normal file
59
extension/examples/router/components/TodoTextInput.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
class TodoTextInput extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
text: this.props.text || '',
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
const text = e.target.value.trim();
|
||||
if (e.which === 13) {
|
||||
this.props.onSave(text);
|
||||
if (this.props.newTodo) {
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
this.setState({ text: e.target.value });
|
||||
}
|
||||
|
||||
handleBlur(e) {
|
||||
if (!this.props.newTodo) {
|
||||
this.props.onSave(e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<input
|
||||
className={classnames({
|
||||
edit: this.props.editing,
|
||||
'new-todo': this.props.newTodo,
|
||||
})}
|
||||
type="text"
|
||||
placeholder={this.props.placeholder}
|
||||
autoFocus="true"
|
||||
value={this.state.text}
|
||||
onBlur={this.handleBlur.bind(this)}
|
||||
onChange={this.handleChange.bind(this)}
|
||||
onKeyDown={this.handleSubmit.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TodoTextInput.propTypes = {
|
||||
onSave: PropTypes.func.isRequired,
|
||||
text: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
editing: PropTypes.bool,
|
||||
newTodo: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default TodoTextInput;
|
||||
6
extension/examples/router/constants/ActionTypes.js
Normal file
6
extension/examples/router/constants/ActionTypes.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export const ADD_TODO = 'ADD_TODO';
|
||||
export const DELETE_TODO = 'DELETE_TODO';
|
||||
export const EDIT_TODO = 'EDIT_TODO';
|
||||
export const COMPLETE_TODO = 'COMPLETE_TODO';
|
||||
export const COMPLETE_ALL = 'COMPLETE_ALL';
|
||||
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
|
||||
3
extension/examples/router/constants/TodoFilters.js
Normal file
3
extension/examples/router/constants/TodoFilters.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const SHOW_ALL = 'show_all';
|
||||
export const SHOW_COMPLETED = 'show_completed';
|
||||
export const SHOW_ACTIVE = 'show_active';
|
||||
38
extension/examples/router/containers/App.js
Normal file
38
extension/examples/router/containers/App.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import Header from '../components/Header';
|
||||
import MainSection from '../components/MainSection';
|
||||
import * as TodoActions from '../actions/todos';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
const { todos, path, actions } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Header addTodo={actions.addTodo} path={path} />
|
||||
<MainSection todos={todos} actions={actions} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
todos: PropTypes.array.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
todos: state.todos,
|
||||
path: state.router.location.pathname,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(TodoActions, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
||||
21
extension/examples/router/containers/Root.js
Normal file
21
extension/examples/router/containers/Root.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import { ReduxRouter } from 'redux-router';
|
||||
import Wrapper from './Wrapper';
|
||||
import App from './App';
|
||||
|
||||
class Root extends Component {
|
||||
render() {
|
||||
return (
|
||||
<ReduxRouter>
|
||||
<Redirect from="/" to="Standard Todo" />
|
||||
<Route path="/" component={Wrapper}>
|
||||
<Route path="/:id" component={App} />
|
||||
</Route>
|
||||
</ReduxRouter>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Root;
|
||||
68
extension/examples/router/containers/Wrapper.js
Normal file
68
extension/examples/router/containers/Wrapper.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { pushState } from 'redux-router';
|
||||
import { Route, Link } from 'react-router';
|
||||
import * as TodoActions from '../actions/todos';
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
pushState: bindActionCreators(pushState, dispatch),
|
||||
actions: bindActionCreators(TodoActions, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
@connect((state) => ({}), mapDispatchToProps)
|
||||
class Wrapper extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
handleClick(event) {
|
||||
event.preventDefault();
|
||||
const { actions, pushState } = this.props;
|
||||
const path = event.target.innerText;
|
||||
|
||||
pushState(null, path);
|
||||
console.log('Navigate to', path);
|
||||
|
||||
if (this.timeout) clearInterval(this.timeout);
|
||||
if (path === 'AutoTodo') {
|
||||
console.log('!');
|
||||
this.timeout = setInterval(() => {
|
||||
actions.addTodo('Auto generated task');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
backgroundColor: '#eee',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<a href="#" onClick={this.handleClick}>
|
||||
Standard Todo
|
||||
</a>{' '}
|
||||
|{' '}
|
||||
<a href="#" onClick={this.handleClick}>
|
||||
AutoTodo
|
||||
</a>
|
||||
</div>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Wrapper;
|
||||
10
extension/examples/router/index.html
Normal file
10
extension/examples/router/index.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redux TodoMVC example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="todoapp" id="root"></div>
|
||||
<script src="/static/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
16
extension/examples/router/index.js
Normal file
16
extension/examples/router/index.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import 'babel-polyfill';
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import Root from './containers/Root';
|
||||
import configureStore from './store/configureStore';
|
||||
import 'todomvc-app-css/index.css';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<Root />
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
53
extension/examples/router/package.json
Normal file
53
extension/examples/router/package.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"name": "redux-todomvc-example",
|
||||
"version": "0.0.0",
|
||||
"description": "Redux TodoMVC example",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"test": "NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js",
|
||||
"test:watch": "npm test -- --watch"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rackt/redux.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rackt/redux/issues"
|
||||
},
|
||||
"homepage": "http://rackt.github.io/redux",
|
||||
"dependencies": {
|
||||
"classnames": "^2.1.2",
|
||||
"history": "^1.13.1",
|
||||
"react": "^0.14.0",
|
||||
"react-dom": "^0.14.0",
|
||||
"react-redux": "^4.0.0",
|
||||
"react-router": "^1.0.2",
|
||||
"redux": "^3.0.0",
|
||||
"redux-router": "^1.0.0-beta5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.3.15",
|
||||
"babel-loader": "^6.2.0",
|
||||
"babel-plugin-add-module-exports": "^0.1.1",
|
||||
"babel-plugin-react-transform": "^2.0.0-beta1",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.2.0",
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"babel-preset-stage-0": "^6.3.13",
|
||||
"expect": "^1.8.0",
|
||||
"express": "^4.13.3",
|
||||
"jsdom": "^5.6.1",
|
||||
"mocha": "^2.2.5",
|
||||
"node-libs-browser": "^0.5.2",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react-addons-test-utils": "^0.14.0",
|
||||
"react-transform-hmr": "^1.0.0",
|
||||
"style-loader": "^0.12.3",
|
||||
"todomvc-app-css": "^2.0.1",
|
||||
"webpack": "^1.9.11",
|
||||
"webpack-dev-middleware": "^1.2.0",
|
||||
"webpack-hot-middleware": "^2.2.0"
|
||||
}
|
||||
}
|
||||
10
extension/examples/router/reducers/index.js
Normal file
10
extension/examples/router/reducers/index.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import { routerStateReducer } from 'redux-router';
|
||||
import todos from './todos';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
todos,
|
||||
router: routerStateReducer,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
||||
61
extension/examples/router/reducers/todos.js
Normal file
61
extension/examples/router/reducers/todos.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import {
|
||||
ADD_TODO,
|
||||
DELETE_TODO,
|
||||
EDIT_TODO,
|
||||
COMPLETE_TODO,
|
||||
COMPLETE_ALL,
|
||||
CLEAR_COMPLETED,
|
||||
} from '../constants/ActionTypes';
|
||||
|
||||
const initialState = [
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
];
|
||||
|
||||
export default function todos(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case ADD_TODO:
|
||||
return [
|
||||
{
|
||||
id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
|
||||
completed: false,
|
||||
text: action.text,
|
||||
},
|
||||
...state,
|
||||
];
|
||||
|
||||
case DELETE_TODO:
|
||||
return state.filter((todo) => todo.id !== action.id);
|
||||
|
||||
case EDIT_TODO:
|
||||
return state.map((todo) =>
|
||||
todo.id === action.id
|
||||
? Object.assign({}, todo, { text: action.text })
|
||||
: todo
|
||||
);
|
||||
|
||||
case COMPLETE_TODO:
|
||||
return state.map((todo) =>
|
||||
todo.id === action.id
|
||||
? Object.assign({}, todo, { completed: !todo.completed })
|
||||
: todo
|
||||
);
|
||||
|
||||
case COMPLETE_ALL:
|
||||
const areAllMarked = state.every((todo) => todo.completed);
|
||||
return state.map((todo) =>
|
||||
Object.assign({}, todo, {
|
||||
completed: !areAllMarked,
|
||||
})
|
||||
);
|
||||
|
||||
case CLEAR_COMPLETED:
|
||||
return state.filter((todo) => todo.completed === false);
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
32
extension/examples/router/server.js
Normal file
32
extension/examples/router/server.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
var webpack = require('webpack');
|
||||
var webpackDevMiddleware = require('webpack-dev-middleware');
|
||||
var webpackHotMiddleware = require('webpack-hot-middleware');
|
||||
var config = require('./webpack.config');
|
||||
|
||||
var app = new require('express')();
|
||||
var port = 4002;
|
||||
|
||||
var compiler = webpack(config);
|
||||
app.use(
|
||||
webpackDevMiddleware(compiler, {
|
||||
noInfo: true,
|
||||
publicPath: config.output.publicPath,
|
||||
})
|
||||
);
|
||||
app.use(webpackHotMiddleware(compiler));
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
res.sendFile(__dirname + '/index.html');
|
||||
});
|
||||
|
||||
app.listen(port, function (error) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
console.info(
|
||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||
port,
|
||||
port
|
||||
);
|
||||
}
|
||||
});
|
||||
28
extension/examples/router/store/configureStore.js
Normal file
28
extension/examples/router/store/configureStore.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { createStore, compose } from 'redux';
|
||||
import {
|
||||
reduxReactRouter,
|
||||
routerStateReducer,
|
||||
ReduxRouter,
|
||||
} from 'redux-router';
|
||||
//import createHistory from 'history/lib/createBrowserHistory';
|
||||
import createHistory from 'history/lib/createHashHistory';
|
||||
import rootReducer from '../reducers';
|
||||
|
||||
export default function configureStore(initialState) {
|
||||
let finalCreateStore = compose(
|
||||
reduxReactRouter({ createHistory }),
|
||||
global.devToolsExtension ? global.devToolsExtension() : (f) => f
|
||||
)(createStore);
|
||||
|
||||
const store = finalCreateStore(rootReducer, initialState);
|
||||
|
||||
if (module.hot) {
|
||||
// Enable Webpack hot module replacement for reducers
|
||||
module.hot.accept('../reducers', () => {
|
||||
const nextReducer = require('../reducers');
|
||||
store.replaceReducer(nextReducer);
|
||||
});
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
46
extension/examples/router/test/actions/todos.spec.js
Normal file
46
extension/examples/router/test/actions/todos.spec.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import expect from 'expect';
|
||||
import * as types from '../../constants/ActionTypes';
|
||||
import * as actions from '../../actions/todos';
|
||||
|
||||
describe('todo actions', () => {
|
||||
it('addTodo should create ADD_TODO action', () => {
|
||||
expect(actions.addTodo('Use Redux')).toEqual({
|
||||
type: types.ADD_TODO,
|
||||
text: 'Use Redux',
|
||||
});
|
||||
});
|
||||
|
||||
it('deleteTodo should create DELETE_TODO action', () => {
|
||||
expect(actions.deleteTodo(1)).toEqual({
|
||||
type: types.DELETE_TODO,
|
||||
id: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('editTodo should create EDIT_TODO action', () => {
|
||||
expect(actions.editTodo(1, 'Use Redux everywhere')).toEqual({
|
||||
type: types.EDIT_TODO,
|
||||
id: 1,
|
||||
text: 'Use Redux everywhere',
|
||||
});
|
||||
});
|
||||
|
||||
it('completeTodo should create COMPLETE_TODO action', () => {
|
||||
expect(actions.completeTodo(1)).toEqual({
|
||||
type: types.COMPLETE_TODO,
|
||||
id: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('completeAll should create COMPLETE_ALL action', () => {
|
||||
expect(actions.completeAll()).toEqual({
|
||||
type: types.COMPLETE_ALL,
|
||||
});
|
||||
});
|
||||
|
||||
it('clearCompleted should create CLEAR_COMPLETED action', () => {
|
||||
expect(actions.clearCompleted('Use Redux')).toEqual({
|
||||
type: types.CLEAR_COMPLETED,
|
||||
});
|
||||
});
|
||||
});
|
||||
108
extension/examples/router/test/components/Footer.spec.js
Normal file
108
extension/examples/router/test/components/Footer.spec.js
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import expect from 'expect';
|
||||
import React from 'react';
|
||||
import TestUtils from 'react-addons-test-utils';
|
||||
import Footer from '../../components/Footer';
|
||||
import { SHOW_ALL, SHOW_ACTIVE } from '../../constants/TodoFilters';
|
||||
|
||||
function setup(propOverrides) {
|
||||
const props = Object.assign(
|
||||
{
|
||||
completedCount: 0,
|
||||
activeCount: 0,
|
||||
filter: SHOW_ALL,
|
||||
onClearCompleted: expect.createSpy(),
|
||||
onShow: expect.createSpy(),
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
renderer.render(<Footer {...props} />);
|
||||
const output = renderer.getRenderOutput();
|
||||
|
||||
return {
|
||||
props: props,
|
||||
output: output,
|
||||
};
|
||||
}
|
||||
|
||||
function getTextContent(elem) {
|
||||
const children = Array.isArray(elem.props.children)
|
||||
? elem.props.children
|
||||
: [elem.props.children];
|
||||
|
||||
return children.reduce(function concatText(out, child) {
|
||||
// Children are either elements or text strings
|
||||
return out + (child.props ? getTextContent(child) : child);
|
||||
}, '');
|
||||
}
|
||||
|
||||
describe('components', () => {
|
||||
describe('Footer', () => {
|
||||
it('should render container', () => {
|
||||
const { output } = setup();
|
||||
expect(output.type).toBe('footer');
|
||||
expect(output.props.className).toBe('footer');
|
||||
});
|
||||
|
||||
it('should display active count when 0', () => {
|
||||
const { output } = setup({ activeCount: 0 });
|
||||
const [count] = output.props.children;
|
||||
expect(getTextContent(count)).toBe('No items left');
|
||||
});
|
||||
|
||||
it('should display active count when above 0', () => {
|
||||
const { output } = setup({ activeCount: 1 });
|
||||
const [count] = output.props.children;
|
||||
expect(getTextContent(count)).toBe('1 item left');
|
||||
});
|
||||
|
||||
it('should render filters', () => {
|
||||
const { output } = setup();
|
||||
const [, filters] = output.props.children;
|
||||
expect(filters.type).toBe('ul');
|
||||
expect(filters.props.className).toBe('filters');
|
||||
expect(filters.props.children.length).toBe(3);
|
||||
filters.props.children.forEach(function checkFilter(filter, i) {
|
||||
expect(filter.type).toBe('li');
|
||||
const a = filter.props.children;
|
||||
expect(a.props.className).toBe(i === 0 ? 'selected' : '');
|
||||
expect(a.props.children).toBe(
|
||||
{
|
||||
0: 'All',
|
||||
1: 'Active',
|
||||
2: 'Completed',
|
||||
}[i]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call onShow when a filter is clicked', () => {
|
||||
const { output, props } = setup();
|
||||
const [, filters] = output.props.children;
|
||||
const filterLink = filters.props.children[1].props.children;
|
||||
filterLink.props.onClick({});
|
||||
expect(props.onShow).toHaveBeenCalledWith(SHOW_ACTIVE);
|
||||
});
|
||||
|
||||
it('shouldnt show clear button when no completed todos', () => {
|
||||
const { output } = setup({ completedCount: 0 });
|
||||
const [, , clear] = output.props.children;
|
||||
expect(clear).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should render clear button when completed todos', () => {
|
||||
const { output } = setup({ completedCount: 1 });
|
||||
const [, , clear] = output.props.children;
|
||||
expect(clear.type).toBe('button');
|
||||
expect(clear.props.children).toBe('Clear completed');
|
||||
});
|
||||
|
||||
it('should call onClearCompleted on clear button click', () => {
|
||||
const { output, props } = setup({ completedCount: 1 });
|
||||
const [, , clear] = output.props.children;
|
||||
clear.props.onClick({});
|
||||
expect(props.onClearCompleted).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
50
extension/examples/router/test/components/Header.spec.js
Normal file
50
extension/examples/router/test/components/Header.spec.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import expect from 'expect';
|
||||
import React from 'react';
|
||||
import TestUtils from 'react-addons-test-utils';
|
||||
import Header from '../../components/Header';
|
||||
import TodoTextInput from '../../components/TodoTextInput';
|
||||
|
||||
function setup() {
|
||||
const props = {
|
||||
addTodo: expect.createSpy(),
|
||||
};
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
renderer.render(<Header {...props} />);
|
||||
const output = renderer.getRenderOutput();
|
||||
|
||||
return {
|
||||
props: props,
|
||||
output: output,
|
||||
renderer: renderer,
|
||||
};
|
||||
}
|
||||
|
||||
describe('components', () => {
|
||||
describe('Header', () => {
|
||||
it('should render correctly', () => {
|
||||
const { output } = setup();
|
||||
|
||||
expect(output.type).toBe('header');
|
||||
expect(output.props.className).toBe('header');
|
||||
|
||||
const [h1, input] = output.props.children;
|
||||
|
||||
expect(h1.type).toBe('h1');
|
||||
expect(h1.props.children).toBe('todos');
|
||||
|
||||
expect(input.type).toBe(TodoTextInput);
|
||||
expect(input.props.newTodo).toBe(true);
|
||||
expect(input.props.placeholder).toBe('What needs to be done?');
|
||||
});
|
||||
|
||||
it('should call call addTodo if length of text is greater than 0', () => {
|
||||
const { output, props } = setup();
|
||||
const input = output.props.children[1];
|
||||
input.props.onSave('');
|
||||
expect(props.addTodo.calls.length).toBe(0);
|
||||
input.props.onSave('Use Redux');
|
||||
expect(props.addTodo.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
150
extension/examples/router/test/components/MainSection.spec.js
Normal file
150
extension/examples/router/test/components/MainSection.spec.js
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import expect from 'expect';
|
||||
import React from 'react';
|
||||
import TestUtils from 'react-addons-test-utils';
|
||||
import MainSection from '../../components/MainSection';
|
||||
import TodoItem from '../../components/TodoItem';
|
||||
import Footer from '../../components/Footer';
|
||||
import { SHOW_ALL, SHOW_COMPLETED } from '../../constants/TodoFilters';
|
||||
|
||||
function setup(propOverrides) {
|
||||
const props = Object.assign(
|
||||
{
|
||||
todos: [
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: true,
|
||||
id: 1,
|
||||
},
|
||||
],
|
||||
actions: {
|
||||
editTodo: expect.createSpy(),
|
||||
deleteTodo: expect.createSpy(),
|
||||
completeTodo: expect.createSpy(),
|
||||
completeAll: expect.createSpy(),
|
||||
clearCompleted: expect.createSpy(),
|
||||
},
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
renderer.render(<MainSection {...props} />);
|
||||
const output = renderer.getRenderOutput();
|
||||
|
||||
return {
|
||||
props: props,
|
||||
output: output,
|
||||
renderer: renderer,
|
||||
};
|
||||
}
|
||||
|
||||
describe('components', () => {
|
||||
describe('MainSection', () => {
|
||||
it('should render container', () => {
|
||||
const { output } = setup();
|
||||
expect(output.type).toBe('section');
|
||||
expect(output.props.className).toBe('main');
|
||||
});
|
||||
|
||||
describe('toggle all input', () => {
|
||||
it('should render', () => {
|
||||
const { output } = setup();
|
||||
const [toggle] = output.props.children;
|
||||
expect(toggle.type).toBe('input');
|
||||
expect(toggle.props.type).toBe('checkbox');
|
||||
expect(toggle.props.checked).toBe(false);
|
||||
});
|
||||
|
||||
it('should be checked if all todos completed', () => {
|
||||
const { output } = setup({
|
||||
todos: [
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: true,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
const [toggle] = output.props.children;
|
||||
expect(toggle.props.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('should call completeAll on change', () => {
|
||||
const { output, props } = setup();
|
||||
const [toggle] = output.props.children;
|
||||
toggle.props.onChange({});
|
||||
expect(props.actions.completeAll).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('footer', () => {
|
||||
it('should render', () => {
|
||||
const { output } = setup();
|
||||
const [, , footer] = output.props.children;
|
||||
expect(footer.type).toBe(Footer);
|
||||
expect(footer.props.completedCount).toBe(1);
|
||||
expect(footer.props.activeCount).toBe(1);
|
||||
expect(footer.props.filter).toBe(SHOW_ALL);
|
||||
});
|
||||
|
||||
it('onShow should set the filter', () => {
|
||||
const { output, renderer } = setup();
|
||||
const [, , footer] = output.props.children;
|
||||
footer.props.onShow(SHOW_COMPLETED);
|
||||
const updated = renderer.getRenderOutput();
|
||||
const [, , updatedFooter] = updated.props.children;
|
||||
expect(updatedFooter.props.filter).toBe(SHOW_COMPLETED);
|
||||
});
|
||||
|
||||
it('onClearCompleted should call clearCompleted', () => {
|
||||
const { output, props } = setup();
|
||||
const [, , footer] = output.props.children;
|
||||
footer.props.onClearCompleted();
|
||||
expect(props.actions.clearCompleted).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('onClearCompleted shouldnt call clearCompleted if no todos completed', () => {
|
||||
const { output, props } = setup({
|
||||
todos: [
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
const [, , footer] = output.props.children;
|
||||
footer.props.onClearCompleted();
|
||||
expect(props.actions.clearCompleted.calls.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('todo list', () => {
|
||||
it('should render', () => {
|
||||
const { output, props } = setup();
|
||||
const [, list] = output.props.children;
|
||||
expect(list.type).toBe('ul');
|
||||
expect(list.props.children.length).toBe(2);
|
||||
list.props.children.forEach((item, i) => {
|
||||
expect(item.type).toBe(TodoItem);
|
||||
expect(item.props.todo).toBe(props.todos[i]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter items', () => {
|
||||
const { output, renderer, props } = setup();
|
||||
const [, , footer] = output.props.children;
|
||||
footer.props.onShow(SHOW_COMPLETED);
|
||||
const updated = renderer.getRenderOutput();
|
||||
const [, updatedList] = updated.props.children;
|
||||
expect(updatedList.props.children.length).toBe(1);
|
||||
expect(updatedList.props.children[0].props.todo).toBe(props.todos[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
118
extension/examples/router/test/components/TodoItem.spec.js
Normal file
118
extension/examples/router/test/components/TodoItem.spec.js
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import expect from 'expect';
|
||||
import React from 'react';
|
||||
import TestUtils from 'react-addons-test-utils';
|
||||
import TodoItem from '../../components/TodoItem';
|
||||
import TodoTextInput from '../../components/TodoTextInput';
|
||||
|
||||
function setup(editing = false) {
|
||||
const props = {
|
||||
todo: {
|
||||
id: 0,
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
},
|
||||
editTodo: expect.createSpy(),
|
||||
deleteTodo: expect.createSpy(),
|
||||
completeTodo: expect.createSpy(),
|
||||
};
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
||||
renderer.render(<TodoItem {...props} />);
|
||||
|
||||
let output = renderer.getRenderOutput();
|
||||
|
||||
if (editing) {
|
||||
const label = output.props.children.props.children[1];
|
||||
label.props.onDoubleClick({});
|
||||
output = renderer.getRenderOutput();
|
||||
}
|
||||
|
||||
return {
|
||||
props: props,
|
||||
output: output,
|
||||
renderer: renderer,
|
||||
};
|
||||
}
|
||||
|
||||
describe('components', () => {
|
||||
describe('TodoItem', () => {
|
||||
it('initial render', () => {
|
||||
const { output } = setup();
|
||||
|
||||
expect(output.type).toBe('li');
|
||||
expect(output.props.className).toBe('');
|
||||
|
||||
const div = output.props.children;
|
||||
|
||||
expect(div.type).toBe('div');
|
||||
expect(div.props.className).toBe('view');
|
||||
|
||||
const [input, label, button] = div.props.children;
|
||||
|
||||
expect(input.type).toBe('input');
|
||||
expect(input.props.checked).toBe(false);
|
||||
|
||||
expect(label.type).toBe('label');
|
||||
expect(label.props.children).toBe('Use Redux');
|
||||
|
||||
expect(button.type).toBe('button');
|
||||
expect(button.props.className).toBe('destroy');
|
||||
});
|
||||
|
||||
it('input onChange should call completeTodo', () => {
|
||||
const { output, props } = setup();
|
||||
const input = output.props.children.props.children[0];
|
||||
input.props.onChange({});
|
||||
expect(props.completeTodo).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('button onClick should call deleteTodo', () => {
|
||||
const { output, props } = setup();
|
||||
const button = output.props.children.props.children[2];
|
||||
button.props.onClick({});
|
||||
expect(props.deleteTodo).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('label onDoubleClick should put component in edit state', () => {
|
||||
const { output, renderer } = setup();
|
||||
const label = output.props.children.props.children[1];
|
||||
label.props.onDoubleClick({});
|
||||
const updated = renderer.getRenderOutput();
|
||||
expect(updated.type).toBe('li');
|
||||
expect(updated.props.className).toBe('editing');
|
||||
});
|
||||
|
||||
it('edit state render', () => {
|
||||
const { output } = setup(true);
|
||||
|
||||
expect(output.type).toBe('li');
|
||||
expect(output.props.className).toBe('editing');
|
||||
|
||||
const input = output.props.children;
|
||||
expect(input.type).toBe(TodoTextInput);
|
||||
expect(input.props.text).toBe('Use Redux');
|
||||
expect(input.props.editing).toBe(true);
|
||||
});
|
||||
|
||||
it('TodoTextInput onSave should call editTodo', () => {
|
||||
const { output, props } = setup(true);
|
||||
output.props.children.props.onSave('Use Redux');
|
||||
expect(props.editTodo).toHaveBeenCalledWith(0, 'Use Redux');
|
||||
});
|
||||
|
||||
it('TodoTextInput onSave should call deleteTodo if text is empty', () => {
|
||||
const { output, props } = setup(true);
|
||||
output.props.children.props.onSave('');
|
||||
expect(props.deleteTodo).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('TodoTextInput onSave should exit component from edit state', () => {
|
||||
const { output, renderer } = setup(true);
|
||||
output.props.children.props.onSave('Use Redux');
|
||||
const updated = renderer.getRenderOutput();
|
||||
expect(updated.type).toBe('li');
|
||||
expect(updated.props.className).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import expect from 'expect';
|
||||
import React from 'react';
|
||||
import TestUtils from 'react-addons-test-utils';
|
||||
import TodoTextInput from '../../components/TodoTextInput';
|
||||
|
||||
function setup(propOverrides) {
|
||||
const props = Object.assign(
|
||||
{
|
||||
onSave: expect.createSpy(),
|
||||
text: 'Use Redux',
|
||||
placeholder: 'What needs to be done?',
|
||||
editing: false,
|
||||
newTodo: false,
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
||||
renderer.render(<TodoTextInput {...props} />);
|
||||
|
||||
let output = renderer.getRenderOutput();
|
||||
|
||||
output = renderer.getRenderOutput();
|
||||
|
||||
return {
|
||||
props: props,
|
||||
output: output,
|
||||
renderer: renderer,
|
||||
};
|
||||
}
|
||||
|
||||
describe('components', () => {
|
||||
describe('TodoTextInput', () => {
|
||||
it('should render correctly', () => {
|
||||
const { output } = setup();
|
||||
expect(output.props.placeholder).toEqual('What needs to be done?');
|
||||
expect(output.props.value).toEqual('Use Redux');
|
||||
expect(output.props.className).toEqual('');
|
||||
});
|
||||
|
||||
it('should render correctly when editing=true', () => {
|
||||
const { output } = setup({ editing: true });
|
||||
expect(output.props.className).toEqual('edit');
|
||||
});
|
||||
|
||||
it('should render correctly when newTodo=true', () => {
|
||||
const { output } = setup({ newTodo: true });
|
||||
expect(output.props.className).toEqual('new-todo');
|
||||
});
|
||||
|
||||
it('should update value on change', () => {
|
||||
const { output, renderer } = setup();
|
||||
output.props.onChange({ target: { value: 'Use Radox' } });
|
||||
const updated = renderer.getRenderOutput();
|
||||
expect(updated.props.value).toEqual('Use Radox');
|
||||
});
|
||||
|
||||
it('should call onSave on return key press', () => {
|
||||
const { output, props } = setup();
|
||||
output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' } });
|
||||
expect(props.onSave).toHaveBeenCalledWith('Use Redux');
|
||||
});
|
||||
|
||||
it('should reset state on return key press if newTodo', () => {
|
||||
const { output, renderer } = setup({ newTodo: true });
|
||||
output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' } });
|
||||
const updated = renderer.getRenderOutput();
|
||||
expect(updated.props.value).toEqual('');
|
||||
});
|
||||
|
||||
it('should call onSave on blur', () => {
|
||||
const { output, props } = setup();
|
||||
output.props.onBlur({ target: { value: 'Use Redux' } });
|
||||
expect(props.onSave).toHaveBeenCalledWith('Use Redux');
|
||||
});
|
||||
|
||||
it('shouldnt call onSave on blur if newTodo', () => {
|
||||
const { output, props } = setup({ newTodo: true });
|
||||
output.props.onBlur({ target: { value: 'Use Redux' } });
|
||||
expect(props.onSave.calls.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
325
extension/examples/router/test/reducers/todos.spec.js
Normal file
325
extension/examples/router/test/reducers/todos.spec.js
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
import expect from 'expect';
|
||||
import todos from '../../reducers/todos';
|
||||
import * as types from '../../constants/ActionTypes';
|
||||
|
||||
describe('todos reducer', () => {
|
||||
it('should handle initial state', () => {
|
||||
expect(todos(undefined, {})).toEqual([
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle ADD_TODO', () => {
|
||||
expect(
|
||||
todos([], {
|
||||
type: types.ADD_TODO,
|
||||
text: 'Run the tests',
|
||||
})
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.ADD_TODO,
|
||||
text: 'Run the tests',
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.ADD_TODO,
|
||||
text: 'Fix the tests',
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Fix the tests',
|
||||
completed: false,
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle DELETE_TODO', () => {
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.DELETE_TODO,
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle EDIT_TODO', () => {
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.EDIT_TODO,
|
||||
text: 'Fix the tests',
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Fix the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle COMPLETE_TODO', () => {
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.COMPLETE_TODO,
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: true,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle COMPLETE_ALL', () => {
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: true,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.COMPLETE_ALL,
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: true,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: true,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
|
||||
// Unmark if all todos are currently completed
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: true,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: true,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.COMPLETE_ALL,
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle CLEAR_COMPLETED', () => {
|
||||
expect(
|
||||
todos(
|
||||
[
|
||||
{
|
||||
text: 'Run the tests',
|
||||
completed: true,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: types.CLEAR_COMPLETED,
|
||||
}
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Use Redux',
|
||||
completed: false,
|
||||
id: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not generate duplicate ids after CLEAR_COMPLETED', () => {
|
||||
expect(
|
||||
[
|
||||
{
|
||||
type: types.COMPLETE_TODO,
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
type: types.CLEAR_COMPLETED,
|
||||
},
|
||||
{
|
||||
type: types.ADD_TODO,
|
||||
text: 'Write more tests',
|
||||
},
|
||||
].reduce(todos, [
|
||||
{
|
||||
id: 0,
|
||||
completed: false,
|
||||
text: 'Use Redux',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
completed: false,
|
||||
text: 'Write tests',
|
||||
},
|
||||
])
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Write more tests',
|
||||
completed: false,
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
text: 'Write tests',
|
||||
completed: false,
|
||||
id: 1,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
5
extension/examples/router/test/setup.js
Normal file
5
extension/examples/router/test/setup.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { jsdom } from 'jsdom';
|
||||
|
||||
global.document = jsdom('<!doctype html><html><body></body></html>');
|
||||
global.window = document.defaultView;
|
||||
global.navigator = global.window.navigator;
|
||||
32
extension/examples/router/webpack.config.js
Normal file
32
extension/examples/router/webpack.config.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
entry: ['webpack-hot-middleware/client', './index'],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/static/',
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoErrorsPlugin(),
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loaders: ['babel'],
|
||||
exclude: /node_modules/,
|
||||
include: __dirname,
|
||||
},
|
||||
{
|
||||
test: /\.css?$/,
|
||||
loaders: ['style', 'raw'],
|
||||
include: __dirname,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
3
extension/examples/saga-counter/.babelrc
Normal file
3
extension/examples/saga-counter/.babelrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["es2015", "stage-0", "react"]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user