Compare commits

...

175 Commits

Author SHA1 Message Date
Nathan Bierema
cd709cf7ed
Remove old examples (#2014) 2026-03-07 21:40:32 -05:00
github-actions[bot]
c6fd44977c
Version Packages (#1960)
* Version Packages

* Trigger build

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2026-03-08 02:24:33 +00:00
Nathan Bierema
648138652a
Convert remaining packages to ESM (#2013)
* redux-devtools-app-core

* redux-devtools-app

* redux-devtools-extension

* redux-devtools-serialize

* Fix patch

* redux-devtools-utils

* redux-devtools-remote

* extension

* Fixes

* Fixes

* Update package versions to major and convert to ESM

Convert remaining packages in the project to ESM.

* Updates

* Fix

* Fix
2026-03-08 02:17:41 +00:00
Nathan Bierema
12849a4982
Convert monitors to ESM (#2012)
* Convert @redux-devtools-dock-monitor to ESM

* chart-monitor

* Updates

* log-monitor

* slider-monitor

* Update

* inspector-monitor

* Updates

* rtk-query-monitor

* test-tab

* Patch it

* Test

* Update

* trace-tab

* Update

* Fix

* Upgrade redux-devtools components to major versions

Updated package versions for several redux-devtools components to major.

* Fix
2026-03-07 17:16:36 -05:00
Nathan Bierema
804d6bd061
Convert @redux-devtools/core to ESM (#2011)
* Convert @redux-devtools/core to ESM

* Change @redux-devtools/core version to major

Convert the @redux-devtools/core package to ESM format.

* Update
2026-03-07 15:33:49 -05:00
Nathan Bierema
3f90241dd7
Convert @redux-devtools/instrument to ESM (#2010)
* Convert @redux-devtools/instrument to ESM

* Update @redux-devtools/instrument version and format

Change version of @redux-devtools/instrument from patch to major and convert to ESM.

* Exports?

* Use exports everywhere

* Use jest.ts

* Fix redux-devtools test

* Updates

* Update
2026-03-07 20:14:09 +00:00
Nathan Bierema
804e729e79
Convert @redux-devtools/ui to ESM (#2009)
* Convert ui to ESM (WIP)

* Finish conversion

* Format

* Patch

* Change @redux-devtools/ui version to major and ESM

Update the version of @redux-devtools/ui to major and convert to ESM.
2026-03-07 19:20:49 +00:00
Nathan Bierema
d61d31a690
Update CodeMirror (#1891)
* Package updates

* Updates

* Keep fonts import

* Update

* Update

* Update snapshot

* Install more packages

* Start work on theming

* Fix build

* Set theme settings

* Set theme styles

* Remove theme prop and update Editor usage

Removed theme prop from Editor and updated usage to wrap in Container for theme context.

* Updates
2026-03-07 16:27:04 +00:00
renovate[bot]
12e561c24f
chore(deps): update dependency copy-webpack-plugin to v14 (#2001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 13:34:31 -05:00
renovate[bot]
d574d6c038
chore(deps): update dependency @types/supertest to v7 (#1999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 13:15:23 -05:00
renovate[bot]
c752dde196
chore(config): migrate config renovate.json (#1997)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 13:56:13 +00:00
renovate[bot]
ab0a944f93
fix(deps): update dependency msw to ^2.12.10 (#1981)
* fix(deps): update dependency msw to ^2.12.10

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2026-03-03 13:38:51 +00:00
renovate[bot]
ed6eb100eb
chore(deps): update actions/upload-artifact action to v7 (#1996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 13:30:16 +00:00
renovate[bot]
bdb08fca6c
chore(deps): lock file maintenance (#1984)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 13:22:42 +00:00
renovate[bot]
e0f127ceea
fix(deps): update all non-major dependencies (#1995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 13:14:57 +00:00
renovate[bot]
55bcccb4b8
fix(deps): update all non-major dependencies (#1988)
* fix(deps): update all non-major dependencies

* Updates

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2026-03-03 08:04:28 -05:00
renovate[bot]
0cd341fe35
fix(deps): update all non-major dependencies (#1983)
* fix(deps): update all non-major dependencies

* Fix tests

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2026-01-15 16:27:23 -05:00
megos
62cb6ddc25
docs(extension): fix broken relative links in README (#1986) 2026-01-07 09:37:52 -05:00
renovate[bot]
ed6d65bd30
chore(deps): update dependency globals to v17 (#1985)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-02 09:54:37 -05:00
renovate[bot]
8f5cf1b5bc
chore(deps): update dependency storybook to ^10.1.10 (#1980)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 21:53:55 +00:00
renovate[bot]
21d5699045
chore(deps): lock file maintenance (#1975)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 21:42:41 +00:00
renovate[bot]
d609bfb14d
fix(deps): update all non-major dependencies (#1972)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 21:26:06 +00:00
renovate[bot]
b350e7bae9
fix(deps): update dependency msw to ^2.12.4 (#1968)
* fix(deps): update dependency msw to ^2.12.4

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-12-24 21:15:59 +00:00
renovate[bot]
35aa092d45
chore(deps): update actions/upload-artifact action to v6 (#1979)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 21:07:17 +00:00
renovate[bot]
102c21d43e
fix(deps): update dependency socketcluster-server to v20 (#1965)
* fix(deps): update dependency socketcluster-client to v20

* fix(deps): update dependency socketcluster-server to v20

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-12-24 20:56:33 +00:00
renovate[bot]
b8219c2afc
fix(deps): update dependency body-parser to ^2.2.1 (#1971)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 15:22:57 +00:00
renovate[bot]
1e701e45cb
chore(deps): lock file maintenance (#1969)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 15:13:05 +00:00
renovate[bot]
78ed3b1efe
fix(deps): update all non-major dependencies (#1966)
* fix(deps): update all non-major dependencies

* Format

* Updates

* Update

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-12-01 09:57:40 -05:00
renovate[bot]
fbdbb56d19
chore(deps): update actions/checkout action to v6 (#1967)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 16:05:50 -05:00
renovate[bot]
c7e77652c2
fix(deps): update dependency @apollo/server to v5 (#1909)
* fix(deps): update dependency @apollo/server to v5

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-11-20 14:53:56 +00:00
renovate[bot]
558e2ecfd3
fix(deps): update dependency msw to ^2.12.2 (#1958)
* fix(deps): update dependency msw to ^2.12.2

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-11-20 14:42:37 +00:00
renovate[bot]
d0e04165ab
chore(deps): lock file maintenance (#1961)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 14:20:24 +00:00
renovate[bot]
aebfca4f93
fix(deps): update all non-major dependencies (#1957)
* fix(deps): update all non-major dependencies

* Fix

* Fix

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-11-20 14:02:42 +00:00
renovate[bot]
56de415db0
fix(deps): update dependency open to v11 (#1959)
* fix(deps): update dependency open to v11

* Update Node.js engine requirement to version 20

* Update @redux-devtools/cli to major version

Updated @redux-devtools/cli dependency version to major and required Node.js 20.

* Format

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-11-15 13:00:49 +00:00
renovate[bot]
36f1f36471
fix(deps): update dependency msw to ^2.12.1 (#1956)
* fix(deps): update dependency msw to ^2.12.1

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-11-12 15:03:38 +00:00
renovate[bot]
1039d6c827
chore(deps): lock file maintenance (#1955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 14:27:26 +00:00
renovate[bot]
ebaf7cf102
fix(deps): update all non-major dependencies (#1953)
* fix(deps): update all non-major dependencies

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-11-12 08:50:25 -05:00
renovate[bot]
a4343c3116
fix(deps): update rjsf monorepo to v6 (major) (#1954)
* fix(deps): update rjsf monorepo to v6

* Update snapshots

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-10-31 23:55:00 +00:00
renovate[bot]
75cafed485
chore(deps): update storybook monorepo to v10 (major) (#1951)
* chore(deps): update storybook monorepo to v10

* ESM updates

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-10-30 14:31:34 -04:00
renovate[bot]
a663401e42
chore(deps): update dependency vite to ^7.1.12 (#1952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 15:18:37 +00:00
renovate[bot]
60a027e016
chore(deps): lock file maintenance (#1942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 10:32:34 -04:00
renovate[bot]
85e0eb9a67
fix(deps): update all non-major dependencies (#1941)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 10:14:30 -04:00
renovate[bot]
8e6014f9d3
fix(deps): update dependency msw to ^2.11.6 (#1943)
* fix(deps): update dependency msw to ^2.11.6

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-10-30 14:02:39 +00:00
renovate[bot]
70c98a0c05
chore(deps): update dependency @types/node to v24 (#1949)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 11:44:48 -04:00
renovate[bot]
ebaf3e0516
chore(deps): update actions/upload-artifact action to v5 (#1948)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 11:06:26 -04:00
renovate[bot]
10f112c0ee
chore(deps): update dependency vite to ^7.1.10 (#1940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 15:11:24 +00:00
renovate[bot]
ca3782c545
chore(deps): update dependency eslint-plugin-react-hooks to v7 (#1937)
* chore(deps): update dependency eslint-plugin-react-hooks to v7

* Update

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-10-14 14:58:12 +00:00
renovate[bot]
9d4924ef1d
chore(deps): update actions/setup-node action to v6 (#1939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-10-14 14:13:52 +00:00
renovate[bot]
16087352e1
fix(deps): update dependency msw to ^2.11.5 (#1933)
* fix(deps): update dependency msw to ^2.11.5

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-10-14 10:05:45 -04:00
renovate[bot]
f80b555457
chore(deps): lock file maintenance (#1935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 09:36:22 -04:00
renovate[bot]
c7a9b746d5
fix(deps): update all non-major dependencies (#1929)
* fix(deps): update all non-major dependencies

* isolatedModules

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-10-14 09:03:32 -04:00
renovate[bot]
f0330162e6
chore(deps): update dependency @types/uuid to v11 (#1930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-16 22:18:55 +00:00
github-actions[bot]
531aa338e7
Version Packages (#1884)
* Version Packages

* Trigger build

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-09-15 15:52:06 +00:00
renovate[bot]
8308db6832
fix(deps): update dependency msw to ^2.11.2 (#1905)
* fix(deps): update dependency msw to ^2.11.2

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-09-15 15:25:27 +00:00
renovate[bot]
4e44c2ca50
fix(deps): update dependency uuid to v13 (#1926)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-09-15 15:05:51 +00:00
renovate[bot]
4e81dc99c4
chore(deps): lock file maintenance (#1925)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 14:52:46 +00:00
renovate[bot]
e9e6b33a6d
chore(deps): update all non-major dependencies to ^9.1.6 (#1928)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 14:26:00 +00:00
renovate[bot]
20883e5bdf
fix(deps): update all non-major dependencies (#1918)
* fix(deps): update all non-major dependencies

* Update non-major dependencies in changeset

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-09-15 14:03:16 +00:00
renovate[bot]
0e9528ea37
chore(deps): update dependency vite to v7.1.5 [security] (#1927)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 17:48:00 -04:00
renovate[bot]
e327727f91
fix(deps): update dependency uuid to v12 (#1924)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 14:19:16 -04:00
renovate[bot]
4a616983aa
chore(deps): update actions/setup-node action to v5 (#1922)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-04 08:35:02 -04:00
renovate[bot]
13e65e0f73
chore(deps): lock file maintenance (#1904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-09-01 16:26:37 +00:00
renovate[bot]
c3e35815d8
chore(deps): update all non-major dependencies (#1917)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 12:18:56 -04:00
renovate[bot]
b1e2d4bd58
fix(deps): update all non-major dependencies (#1901)
* fix(deps): update all non-major dependencies

* Update snapshots

* Update snpashots

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-09-01 11:57:43 -04:00
renovate[bot]
68f82b6994
chore(deps): update actions/checkout action to v5 (#1914)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 12:48:53 -04:00
renovate[bot]
30308f54ab
chore(deps): update dependency cross-env to v10 (#1910)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-08-08 01:05:33 +00:00
renovate[bot]
5b28b69261
chore(deps): update dependency typescript to ~5.9.2 (#1912)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-07 20:57:36 -04:00
Nathan Bierema
7a48268a74
Dedupe pnpm (#1903) 2025-06-28 14:10:19 +00:00
renovate[bot]
fa20f09fc1
chore(deps): update dependency vite to v7 (#1899)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 13:06:49 +00:00
renovate[bot]
1e0d09251b
chore(deps): lock file maintenance (#1898)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 12:57:06 +00:00
renovate[bot]
2b7e1b890a
fix(deps): update all non-major dependencies (#1894)
* fix(deps): update all non-major dependencies

* Format

* Dedupe

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-06-24 08:46:36 -04:00
renovate[bot]
119864cd95
chore(deps): update dependency eslint-plugin-jest to v29 (#1896)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 09:23:54 -04:00
renovate[bot]
dfa6c937c3
chore(deps): update jest monorepo to v30 (major) (#1893)
* chore(deps): update jest monorepo to v30

* Update snapshot

* Update

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-06-17 20:20:57 -04:00
renovate[bot]
8b554db5bf
chore(deps): lock file maintenance (#1886)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 00:06:25 +00:00
renovate[bot]
842419b802
fix(deps): update all non-major dependencies (#1872)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-17 23:53:45 +00:00
renovate[bot]
1e3fb5e27a
fix(deps): update dependency express to v5 (#1779)
* fix(deps): update dependency express to v5

* Update

* Lint

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-06-17 23:43:28 +00:00
renovate[bot]
da5e9f0c1a
fix(deps): update dependency msw to ^2.10.2 (#1888)
* fix(deps): update dependency msw to ^2.10.2

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-06-17 23:32:57 +00:00
Nathan Bierema
54c40135e5
Use Chrome for Testing for Selenium testing (#1892)
* Use Chrome for Testing to fix Selenium tests

* Unintentional
2025-06-17 23:03:29 +00:00
Nathan Bierema
61632768a7
Replace styled-components with Emotion (#1883)
* Replace styled-components with Emotion in ui

* react-dock

* Remainder

* Fix

* Format

* Update snapshots

* Create bright-sheep-joke.md
2025-06-01 13:59:13 +00:00
renovate[bot]
585d6b9220
fix(deps): update dependency @chakra-ui/react to v3 (#1802)
* fix(deps): update dependency @chakra-ui/react to v3

* Remove unnecessary packages

* Add snippets

* Updates

* Remove unused

* Remove ColorModeProvider?

* Fix

* Fix

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-31 18:33:19 +00:00
renovate[bot]
6468c48b75
fix(deps): update dependency react-router-dom to v7 (#1881)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 16:10:19 +00:00
renovate[bot]
41f3c1a7b6
fix(deps): update dependency framer-motion to v12 (#1880)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 15:59:03 +00:00
renovate[bot]
c82e511929
fix(deps): update react monorepo (major) (#1801)
* fix(deps): update react monorepo

* Updates

* Update

* Update

* Update

* Updates

* Updates

* Update

* Remove usages of react-test-renderer

* Fix

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-31 14:34:45 +00:00
renovate[bot]
832f4dc195
chore(deps): update storybook monorepo to v9 (major) (#1876)
* chore(deps): update storybook monorepo to v9

* Updates

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-31 13:24:58 +00:00
renovate[bot]
4b35476610
fix(deps): update dependency msw to ^2.8.7 (#1874)
* fix(deps): update dependency msw to ^2.8.7

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-31 12:52:38 +00:00
renovate[bot]
7873dd23ae
chore(deps): update pnpm to v10 (#1868)
* chore(deps): update pnpm to v10

* Build?

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-31 12:34:05 +00:00
renovate[bot]
9444ca7e87
fix(deps): update dnd-kit monorepo (#1871)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-23 23:09:17 +00:00
renovate[bot]
b2f01026cb
fix(deps): update dependency uuid to v11 (#1870)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-23 22:54:20 +00:00
renovate[bot]
04c234dd49
fix(deps): update dependency color to v5 (#1869)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-23 22:28:15 +00:00
renovate[bot]
c394803622
chore(deps): update dependency stylelint-config-standard to v38 (#1867)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-23 18:02:31 -04:00
renovate[bot]
fd56ac7d92
chore(deps): update dependency globals to v16 (#1866)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-23 21:23:59 +00:00
renovate[bot]
65dce0864e
chore(deps): update dependency copy-webpack-plugin to v13 (#1865)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-23 21:13:20 +00:00
renovate[bot]
e02cfd327c
fix(deps): update dependency msw to ^2.8.4 (#1856)
* fix(deps): update dependency msw to ^2.8.4

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-23 21:02:42 +00:00
renovate[bot]
9ddcef672f
chore(deps): lock file maintenance (#1864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-23 20:50:40 +00:00
renovate[bot]
3f52f16473
fix(deps): update all non-major dependencies (#1861)
* fix(deps): update all non-major dependencies

* Dedupe

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-23 20:31:45 +00:00
renovate[bot]
9f12777827
chore(deps): lock file maintenance (#1860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-08 02:45:30 +00:00
renovate[bot]
5dc8611c38
fix(deps): update all non-major dependencies (#1843)
* fix(deps): update all non-major dependencies

* Fix lint

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-05-08 02:27:02 +00:00
github-actions[bot]
1c5df1ee32
Version Packages (#1851)
* Version Packages

* Bump extension version number

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-04-03 00:36:05 +00:00
renovate[bot]
3fc48a226b
fix(deps): update dependency @babel/runtime to v7.26.10 [security] (#1850)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-02 18:56:21 +00:00
Mark Erikson
17b55ef99f
Handle api.provided state changes in RTKQ 2.6.2 (#1848)
* Handle api.provided state changes in RTKQ 2.6.2

* Create little-melons-grow.md

---------

Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-04-02 18:46:52 +00:00
renovate[bot]
73a01cc5a7
chore(deps): update dependency @storybook/addon-webpack5-compiler-swc to v3 (#1849)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-02 18:38:41 +00:00
renovate[bot]
07e8f2c3ad
chore(deps): update dependency babel-loader to v10 (#1845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 17:23:47 -05:00
renovate[bot]
8c563d71bb
chore(deps): update dependency typescript to ~5.8.2 (#1844)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 22:10:41 +00:00
renovate[bot]
d730ea185f
fix(deps): update dependency immutable to v5 (#1799)
* fix(deps): update dependency immutable to v5

* Updates

* Updates

* Updates

* Fix lint

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-03-05 22:00:27 +00:00
renovate[bot]
6df66b7320
fix(deps): update dependency msw to ^2.7.3 (#1841)
* fix(deps): update dependency msw to ^2.7.3

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-03-05 20:40:41 +00:00
renovate[bot]
3c90662cfa
chore(deps): lock file maintenance (#1842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 14:49:14 -05:00
renovate[bot]
763bf937a4
fix(deps): update all non-major dependencies (#1840)
* fix(deps): update all non-major dependencies

* Dedupe

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-03-05 19:15:25 +00:00
github-actions[bot]
8ec2b303ee
Version Packages (#1838)
* Version Packages

* Trigger build

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-03-01 16:37:28 -05:00
Nathan Bierema
91f21b2ffc
Fix compatibility of createDevTools with React 19 types (#1837)
* Fix compatibility of createDevTools with React 19 types

* Create fluffy-keys-doubt.md
2025-03-01 20:09:01 +00:00
github-actions[bot]
ff60266836
Version Packages (#1836)
* Version Packages

* Fix version numbers

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-03-01 12:09:02 -05:00
Nathan Bierema
6830118951
Add React 19 to peer deps (#1835)
* Add React 19 to peer deps

* Create moody-crabs-kick.md

* Update moody-crabs-kick.md
2025-03-01 11:44:15 -05:00
renovate[bot]
cc7ec13fb9
chore(deps): lock file maintenance (#1834)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 21:53:49 +00:00
renovate[bot]
e46bbcaef3
fix(deps): update all non-major dependencies (#1833)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 16:40:12 -05:00
renovate[bot]
4a8fbc675a
chore(deps): lock file maintenance (#1830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 16:06:50 -05:00
renovate[bot]
c9bed44e0e
fix(deps): update all non-major dependencies (#1826)
* fix(deps): update all non-major dependencies

* Format

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-02-10 20:46:53 +00:00
renovate[bot]
750046c4d9
chore(deps): update dependency stylelint-config-standard to v37 (#1824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-29 20:59:43 +00:00
renovate[bot]
f42403c579
chore(deps): update dependency eslint-config-prettier to v10 (#1822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-01-29 19:57:40 +00:00
renovate[bot]
519ec090c6
chore(deps): lock file maintenance (#1823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-29 19:46:48 +00:00
renovate[bot]
4e71048997
fix(deps): update all non-major dependencies (#1814)
* fix(deps): update all non-major dependencies

* Dedupe

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-01-29 19:31:18 +00:00
renovate[bot]
5b33056bc5
chore(deps): update dependency webpack-cli to v6 (#1812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-09 15:18:55 +00:00
renovate[bot]
da0051706e
chore(deps): lock file maintenance (#1813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-09 10:11:21 -05:00
renovate[bot]
6ea51af67c
fix(deps): update all non-major dependencies (#1810)
* fix(deps): update all non-major dependencies

* Dedupe

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2025-01-09 14:45:25 +00:00
renovate[bot]
9a46407254
fix(deps): update dependency msw to ^2.7.0 (#1800)
* fix(deps): update dependency msw to ^2.7.0

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-12-30 13:24:34 -05:00
renovate[bot]
07bd4476b2
chore(deps): update dependency @storybook/addon-webpack5-compiler-swc to v2 (#1807)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-30 17:13:48 +00:00
renovate[bot]
b9993eb5f7
chore(deps): lock file maintenance (#1808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-30 12:03:27 -05:00
renovate[bot]
5228d46328
fix(deps): update all non-major dependencies (#1794)
* fix(deps): update all non-major dependencies

* Downgrade Ubuntu

Possibly related:

- https://github.com/electron/electron/issues/41066
- https://github.com/SeleniumHQ/selenium/issues/14609

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-12-30 10:48:57 -05:00
Nathan Bierema
2002a81071 Publish @redux-devtools/rtk-query-monitor 2024-12-16 12:43:15 -05:00
renovate[bot]
c065b03caa
chore(deps): update eslint monorepo to v9 (#1670)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-12-07 00:03:15 +00:00
renovate[bot]
81a9ee33cc
chore(deps): update dependency @types/color to v4 (#1795)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-12-06 23:45:59 +00:00
renovate[bot]
3c47910110
chore(deps): update dependency @chromatic-com/storybook to v3 (#1787)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-12-06 23:34:12 +00:00
renovate[bot]
1c6f45fb47
chore(deps): update dependency typescript to ~5.7.2 (#1757)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-06 23:25:49 +00:00
renovate[bot]
054b27a9f8
chore(deps): update dependency @types/node to v22 (#1796)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-06 23:14:52 +00:00
renovate[bot]
415257cd41
fix(deps): update dependency msw to ^2.6.7 (#1763)
* fix(deps): update dependency msw to ^2.6.7

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-12-06 23:03:53 +00:00
renovate[bot]
14201108e7
chore(deps): lock file maintenance (#1773)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-06 22:53:43 +00:00
renovate[bot]
90737b7e26
fix(deps): update all non-major dependencies (#1780)
* fix(deps): update all non-major dependencies

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-12-06 22:39:17 +00:00
Nathan Bierema
54c6f26c81 Publish @redux-devtools/utils
This hasn't been published since the upgrade to Redux 5
2024-12-05 15:57:27 -05:00
takanuva15
6e400f68b3
feat(docs): add reference to new intellij redux devtools plugin (#1788) 2024-11-19 20:57:04 -05:00
renovate[bot]
2c65192b4f
fix(deps): update all non-major dependencies (#1778)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 18:54:50 +00:00
renovate[bot]
88a02a8332
chore(deps): update all non-major dependencies (#1764)
* chore(deps): update all non-major dependencies

* Combine @types/lodash

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-10-14 08:51:59 -04:00
github-actions[bot]
04858cd514
Version Packages (#1772)
* Version Packages

* Bump version

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-21 15:31:15 +00:00
Nathan Bierema
b25bf1304b
Send state from background when monitor connects (#1771)
* Send state from background on connection

* Create green-hats-kick.md
2024-09-21 15:17:43 +00:00
Nathan Bierema
41fae27637
Use pnpm for running scripts instead of nx (#1770)
* Use pnpm for running scripts instead of nx

* Fix typos
2024-09-20 03:06:32 +00:00
Nathan Bierema
fdce076757
Enable linting for extension (#1769)
* Enable linting for extension

* Update lock file
2024-09-20 02:51:22 +00:00
github-actions[bot]
b934e80d23
Version Packages (#1768)
* Version Packages

* Update

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-20 01:23:20 +00:00
Nathan Bierema
50d7682776
Fix DevTools from losing connection (#1767)
* Fix DevTools from losing connection

* Create kind-seals-arrive.md
2024-09-20 01:07:52 +00:00
renovate[bot]
344387c9c6
chore(deps): lock file maintenance (#1745)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-16 20:48:34 -04:00
renovate[bot]
80c570d6d0
fix(deps): update dependency msw to ^2.4.7 (#1762)
* fix(deps): update dependency msw to ^2.4.7

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-17 00:19:53 +00:00
renovate[bot]
aca0cd09a1
chore(deps): update dependency @chromatic-com/storybook to v2 (#1756)
* chore(deps): update dependency @chromatic-com/storybook to v2

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-16 23:44:52 +00:00
renovate[bot]
75dbf74963
chore(deps): update all non-major dependencies (#1746)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-16 19:24:11 -04:00
github-actions[bot]
997f7b636d
Version Packages (#1761)
* Version Packages

* Bump

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-16 02:57:02 +00:00
Nathan Bierema
eb3ac09b03
Add logging to background service worker (#1760)
* Add logging in background service worker

* Create ninety-sheep-end.md
2024-09-16 02:19:01 +00:00
renovate[bot]
3c39eb49f2
fix(deps): update dependency msw to ^2.4.4 (#1750)
* fix(deps): update dependency msw to ^2.4.4

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-09 16:49:21 -04:00
github-actions[bot]
61c09e1cc3
Version Packages (#1749)
* Version Packages

* Bump

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-04 02:07:20 +00:00
Nathan Bierema
f1d61580a8
Fix mocking Chrome API for Electron (#1748)
* Fix mocking Chrome API for Electron

* Create chilled-feet-marry.md
2024-09-04 01:47:16 +00:00
renovate[bot]
c5aef77b85
chore(deps): lock file maintenance (#1744)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 14:02:40 +00:00
renovate[bot]
908e1c11bd
chore(deps): update pnpm to v9 (#1679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 09:41:09 -04:00
renovate[bot]
d591c18fc8
fix(deps): update dependency msw to ^2.4.1 (#1733)
* fix(deps): update dependency msw to ^2.4.1

* Update msw

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-09-01 13:24:59 +00:00
renovate[bot]
bc4b0755c3
chore(deps): lock file maintenance (#1728)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 12:57:20 +00:00
renovate[bot]
2c8f0a0544
chore(deps): update dependency @types/node to ^20.16.3 (#1743)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 12:43:37 +00:00
renovate[bot]
68d4440e38
chore(deps): update all non-major dependencies (#1729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 08:30:09 -04:00
Nathan Bierema
c52962d532
Use workspace protocol (#1742) 2024-09-01 04:12:24 +00:00
Nathan Bierema
18b86498e2
Remove workspaces from package.json (#1741) 2024-08-31 22:26:53 -04:00
Nathan Bierema
01ee4e99be
Update pnpm setup GitHub Action (#1740) 2024-08-31 21:55:32 +00:00
github-actions[bot]
c133d59461
Version Packages (#1738)
* Version Packages

* Update

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-08-31 21:23:43 +00:00
Nathan Bierema
fd9f9504f0
Fix monitoring on opening panel (#1739)
* Fix monitoring on opening panel

* Create polite-foxes-rest.md

* Further cleanup

* Fix

* Simplify

* ===
2024-08-31 21:10:34 +00:00
Nathan Bierema
e49708d831
Fix manifest.json for Edge (#1737)
* Fix manifest.json for Edge

* Create unlucky-dots-hammer.md
2024-08-31 13:22:13 +00:00
github-actions[bot]
f64cbda982
Version Packages (#1736)
* Version Packages

* Bump

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-08-31 03:38:12 +00:00
Nathan Bierema
abd03a70c7
Fix: only send data to extension if DevTools are open (#1735)
* Fix: only send data to extension if DevTools are open

* Create odd-apples-argue.md
2024-08-30 23:26:39 -04:00
Nathan Bierema
b3e8f209fd
Remove more unnecessary use of lodash (#1734) 2024-08-30 19:07:05 -04:00
renovate[bot]
238a38fb21
fix(deps): update dependency @rjsf/core to v5 (#1409)
* fix(deps): update dependency @rjsf/core to v5

* redux-devtools-ui

* Update

* Update

* Update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-08-26 03:16:10 +00:00
renovate[bot]
76183cfa43
chore(deps): lock file maintenance (#1715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-26 02:11:43 +00:00
renovate[bot]
9fa9a6ff79
fix(deps): update all non-major dependencies (#1713)
* fix(deps): update all non-major dependencies

* weird

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-08-26 01:53:07 +00:00
github-actions[bot]
2a93de46a1
Version Packages (#1720)
* Version Packages

* Bump version numbers

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2024-08-17 19:25:57 +00:00
Nathan Bierema
83b2c19a11
Upgrade to Manifest V3 (#1714)
* Update Chrome manifest.json

* Remove use of window in background

* Test devpanel

* Inject pageScript using new API

* Keep connection from devpanel to background alive

* Keep connection from content script to background alive

* Replace page action with action

* Cleanup syncOptions

* Update options to not rely on background page access

* Start work on updating popup

* Updates

* Remove window

* Get opening in a separate window working

* Remove pageScriptWrap

* Add socket to panelStore

* Fix tests

* Try to use MV3 for Firefox

* Fix path

* Fix Chrome E2E tests

* Revert unintentional change

* Skip Electron tests for now

Looks like they're still working through stuff in https://github.com/electron/electron/issues/41613

* Better image centering

The Firefox popup did not like the old CSS. This is still not perfect, but it's better than it was.

* Create shaggy-taxis-cross.md
2024-08-17 19:11:46 +00:00
Nathan Bierema
61ec00f505
Remove unnecessary lodash usage from bundles (#1718)
* Use lodash-es instead of lodash in extension

Produces (slightly) smaller bundles

* Use lodash-es instead of lodash in instrument

* Use lodash-es instead of lodash in utils

* Use lodash-es instead of lodash in redux-devtools

* Remove lodash from instrument

* Remove lodash from redux-devtools

* Remove lodash from utils

* Remove unnecessary mapValues from extension
2024-08-13 23:42:16 -04:00
717 changed files with 19909 additions and 27875 deletions

View File

@ -8,15 +8,12 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: 'ubuntu-22.04'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: - uses: pnpm/action-setup@v4
fetch-depth: 0 - uses: actions/setup-node@v6
- uses: nrwl/nx-set-shas@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with: with:
node-version: 'lts/*' node-version: 'lts/*'
cache: 'pnpm' cache: 'pnpm'

View File

@ -10,18 +10,18 @@ permissions: write-all
jobs: jobs:
release: release:
name: Release name: Release
runs-on: ubuntu-latest runs-on: 'ubuntu-22.04'
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0 fetch-depth: 0
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v4
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 'lts/*' node-version: 'lts/*'
cache: 'pnpm' cache: 'pnpm'
@ -40,19 +40,19 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Archive Chrome Extension - name: Archive Chrome Extension
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: chrome name: chrome
path: extension/chrome/dist path: extension/chrome/dist
- name: Archive Edge Extension - name: Archive Edge Extension
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: edge name: edge
path: extension/edge/dist path: extension/edge/dist
- name: Archive Firefox Extension - name: Archive Firefox Extension
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: firefox name: firefox
path: extension/firefox/dist path: extension/firefox/dist

View File

@ -24,7 +24,7 @@ It can be used as a browser extension (for [Chrome](https://chrome.google.com/we
## Development ## Development
This is a monorepo powered by [pnpm](https://pnpm.io/) and [Nx](https://nx.dev/). [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 Nx commands to build only the packages necessary for the packages you're working on (i.e., `pnpm nx build remotedev-redux-devtools-extension`). 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 ## Backers

View File

@ -1,4 +1,5 @@
import { defineConfig } from 'eslint/config';
import eslint from '@eslint/js'; import eslint from '@eslint/js';
import eslintConfigPrettier from 'eslint-config-prettier'; import eslintConfigPrettier from 'eslint-config-prettier';
export default [eslint.configs.recommended, eslintConfigPrettier]; export default defineConfig([eslint.configs.recommended, eslintConfigPrettier]);

View File

@ -0,0 +1,41 @@
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,
},
]);

View File

@ -1,8 +1,10 @@
import { defineConfig } from 'eslint/config';
import eslint from '@eslint/js'; import eslint from '@eslint/js';
import tseslint from 'typescript-eslint'; import tseslint from 'typescript-eslint';
import eslintConfigPrettier from 'eslint-config-prettier'; import eslintConfigPrettier from 'eslint-config-prettier';
export default (tsconfigRootDir, files = ['**/*.ts'], project = true) => [ export default (tsconfigRootDir, files = ['**/*.ts'], project = true) =>
defineConfig([
{ {
files, files,
...eslint.configs.recommended, ...eslint.configs.recommended,
@ -52,4 +54,4 @@ export default (tsconfigRootDir, files = ['**/*.ts'], project = true) => [
'@typescript-eslint/prefer-function-type': 'off', '@typescript-eslint/prefer-function-type': 'off',
}, },
}, },
]; ]);

View File

@ -1,9 +1,11 @@
import { defineConfig } from 'eslint/config';
import eslint from '@eslint/js'; import eslint from '@eslint/js';
import tseslint from 'typescript-eslint'; import tseslint from 'typescript-eslint';
import jest from 'eslint-plugin-jest'; import jest from 'eslint-plugin-jest';
import eslintConfigPrettier from 'eslint-config-prettier'; import eslintConfigPrettier from 'eslint-config-prettier';
export default (tsconfigRootDir) => [ export default (tsconfigRootDir) =>
defineConfig([
{ {
files: ['test/**/*.ts'], files: ['test/**/*.ts'],
...eslint.configs.recommended, ...eslint.configs.recommended,
@ -61,4 +63,4 @@ export default (tsconfigRootDir) => [
'@typescript-eslint/prefer-function-type': 'off', '@typescript-eslint/prefer-function-type': 'off',
}, },
}, },
]; ]);

View File

@ -1,15 +1,16 @@
import { defineConfig } from 'eslint/config';
import eslint from '@eslint/js'; import eslint from '@eslint/js';
import tseslint from 'typescript-eslint'; import tseslint from 'typescript-eslint';
import react from 'eslint-plugin-react'; import react from 'eslint-plugin-react';
import { fixupPluginRules } from '@eslint/compat'; import reactHooks from 'eslint-plugin-react-hooks';
import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
import eslintConfigPrettier from 'eslint-config-prettier'; import eslintConfigPrettier from 'eslint-config-prettier';
export default ( export default (
tsconfigRootDir, tsconfigRootDir,
files = ['**/*.ts', '**/*.tsx'], files = ['**/*.ts', '**/*.tsx'],
project = true, project = true,
) => [ ) =>
defineConfig([
{ {
files, files,
...eslint.configs.recommended, ...eslint.configs.recommended,
@ -45,9 +46,7 @@ export default (
}, },
{ {
files, files,
plugins: { ...reactHooks.configs.flat.recommended,
'react-hooks': fixupPluginRules(eslintPluginReactHooks),
},
}, },
{ {
files, files,
@ -86,4 +85,4 @@ export default (
'react/prop-types': 'off', 'react/prop-types': 'off',
}, },
}, },
]; ]);

View File

@ -1,12 +1,13 @@
import { defineConfig } from 'eslint/config';
import eslint from '@eslint/js'; import eslint from '@eslint/js';
import tseslint from 'typescript-eslint'; import tseslint from 'typescript-eslint';
import react from 'eslint-plugin-react'; import react from 'eslint-plugin-react';
import { fixupPluginRules } from '@eslint/compat'; import reactHooks from 'eslint-plugin-react-hooks';
import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
import jest from 'eslint-plugin-jest'; import jest from 'eslint-plugin-jest';
import eslintConfigPrettier from 'eslint-config-prettier'; import eslintConfigPrettier from 'eslint-config-prettier';
export default (tsconfigRootDir) => [ export default (tsconfigRootDir) =>
defineConfig([
{ {
files: ['test/**/*.ts', 'test/**/*.tsx'], files: ['test/**/*.ts', 'test/**/*.tsx'],
...eslint.configs.recommended, ...eslint.configs.recommended,
@ -42,9 +43,7 @@ export default (tsconfigRootDir) => [
}, },
{ {
files: ['test/**/*.ts', 'test/**/*.tsx'], files: ['test/**/*.ts', 'test/**/*.tsx'],
plugins: { ...reactHooks.configs.flat.recommended,
'react-hooks': fixupPluginRules(eslintPluginReactHooks),
},
}, },
{ {
files: ['test/**/*.ts', 'test/**/*.tsx'], files: ['test/**/*.ts', 'test/**/*.tsx'],
@ -82,4 +81,4 @@ export default (tsconfigRootDir) => [
'@typescript-eslint/prefer-function-type': 'off', '@typescript-eslint/prefer-function-type': 'off',
}, },
}, },
]; ]);

View File

@ -1,3 +0,0 @@
node_modules
dist
examples

View File

@ -1,31 +0,0 @@
{
"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"]
}

View File

@ -1,5 +1,103 @@
# remotedev-redux-devtools-extension # remotedev-redux-devtools-extension
## 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 ## 3.1.11
### Patch Changes ### Patch Changes

View File

@ -57,7 +57,7 @@ const composeEnhancers =
compose; 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-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).
```js ```js
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
@ -228,7 +228,7 @@ See [integrations](docs/Integrations.md) and [the blog post](https://medium.com/
- [Methods (advanced API)](docs/API/Methods.md) - [Methods (advanced API)](docs/API/Methods.md)
- [FAQ](docs/FAQ.md) - [FAQ](docs/FAQ.md)
- Features - Features
- [Trace actions calls](/docs/Features/Trace.md) - [Trace actions calls](docs/Features/Trace.md)
- [Troubleshooting](docs/Troubleshooting.md) - [Troubleshooting](docs/Troubleshooting.md)
- [Articles](docs/Articles.md) - [Articles](docs/Articles.md)
- [Videos](docs/Videos.md) - [Videos](docs/Videos.md)

View File

@ -5,7 +5,7 @@ import pug from 'pug';
const args = process.argv.slice(2); const args = process.argv.slice(2);
const prod = !args.includes('--dev'); const prod = !args.includes('--dev');
const commonEsbuildOptions = { await esbuild.build({
bundle: true, bundle: true,
logLevel: 'info', logLevel: 'info',
outdir: 'dist', outdir: 'dist',
@ -15,40 +15,24 @@ const commonEsbuildOptions = {
'process.env.NODE_ENV': prod ? '"production"' : '"development"', 'process.env.NODE_ENV': prod ? '"production"' : '"development"',
'process.env.BABEL_ENV': prod ? '"production"' : '"development"', 'process.env.BABEL_ENV': prod ? '"production"' : '"development"',
}, },
};
await esbuild.build({
...commonEsbuildOptions,
entryPoints: [ entryPoints: [
{ out: 'background.bundle', in: 'src/background/index.ts' }, { out: 'background.bundle', in: 'src/background/index.ts' },
{ out: 'options.bundle', in: 'src/options/index.tsx' }, { out: 'options.bundle', in: 'src/options/index.tsx' },
{ out: 'window.bundle', in: 'src/window/index.tsx' },
{ out: 'remote.bundle', in: 'src/remote/index.tsx' }, { out: 'remote.bundle', in: 'src/remote/index.tsx' },
{ out: 'devpanel.bundle', in: 'src/devpanel/index.tsx' }, { out: 'devpanel.bundle', in: 'src/devpanel/index.tsx' },
{ out: 'devtools.bundle', in: 'src/devtools/index.ts' }, { out: 'devtools.bundle', in: 'src/devtools/index.ts' },
{ out: 'content.bundle', in: 'src/contentScript/index.ts' }, { out: 'content.bundle', in: 'src/contentScript/index.ts' },
{ out: 'page.bundle', in: 'src/pageScript/index.ts' }, { out: 'page.bundle', in: 'src/pageScript/index.ts' },
...(prod ? [] : [{ out: 'pagewrap.bundle', in: 'src/pageScriptWrap.ts' }]),
], ],
loader: { loader: {
'.woff2': 'file', '.woff2': 'file',
}, },
}); });
if (prod) {
await esbuild.build({
...commonEsbuildOptions,
entryPoints: [{ out: 'pagewrap.bundle', in: 'src/pageScriptWrap.ts' }],
loader: {
'.js': 'text',
},
});
}
console.log(); console.log();
console.log('Creating HTML files...'); console.log('Creating HTML files...');
const htmlFiles = ['devpanel', 'devtools', 'options', 'remote', 'window']; const htmlFiles = ['devpanel', 'devtools', 'options', 'remote'];
for (const htmlFile of htmlFiles) { for (const htmlFile of htmlFiles) {
fs.writeFileSync( fs.writeFileSync(
`dist/${htmlFile}.html`, `dist/${htmlFile}.html`,

View File

@ -1,28 +1,22 @@
{ {
"version": "3.1.10", "version": "3.2.10",
"name": "Redux DevTools", "name": "Redux DevTools",
"description": "Redux DevTools for debugging application's state changes.", "description": "Redux DevTools for debugging application's state changes.",
"homepage_url": "https://github.com/reduxjs/redux-devtools", "homepage_url": "https://github.com/reduxjs/redux-devtools",
"manifest_version": 2, "manifest_version": 3,
"page_action": { "action": {
"default_icon": "img/logo/gray.png", "default_icon": "img/logo/gray.png",
"default_title": "Redux DevTools", "default_title": "Redux DevTools",
"default_popup": "window.html#popup" "default_popup": "devpanel.html#popup"
}, },
"commands": { "commands": {
"devtools-left": { "devtools-window": {
"description": "DevTools window to left" "description": "DevTools window"
},
"devtools-right": {
"description": "DevTools window to right"
},
"devtools-bottom": {
"description": "DevTools window to bottom"
}, },
"devtools-remote": { "devtools-remote": {
"description": "Remote DevTools" "description": "Remote DevTools"
}, },
"_execute_page_action": { "_execute_action": {
"suggested_key": { "suggested_key": {
"default": "Ctrl+Shift+E" "default": "Ctrl+Shift+E"
} }
@ -34,36 +28,37 @@
"128": "img/logo/128x128.png" "128": "img/logo/128x128.png"
}, },
"options_ui": { "options_ui": {
"page": "options.html", "page": "options.html"
"chrome_style": true
}, },
"background": { "background": {
"scripts": ["background.bundle.js"], "service_worker": "background.bundle.js"
"persistent": false
}, },
"content_scripts": [ "content_scripts": [
{ {
"matches": ["<all_urls>"], "matches": ["<all_urls>"],
"exclude_globs": ["https://www.google*"], "exclude_globs": ["https://www.google*"],
"js": ["content.bundle.js", "pagewrap.bundle.js"], "js": ["content.bundle.js"],
"run_at": "document_start", "run_at": "document_start",
"all_frames": true "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", "devtools_page": "devtools.html",
"web_accessible_resources": ["page.bundle.js"],
"externally_connectable": { "externally_connectable": {
"ids": ["*"] "ids": ["*"]
}, },
"permissions": [ "permissions": ["notifications", "contextMenus", "storage"],
"notifications", "host_permissions": ["file:///*", "http://*/*", "https://*/*"],
"contextMenus", "content_security_policy": {
"storage", "extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
"file:///*", },
"http://*/*",
"https://*/*"
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;",
"update_url": "https://clients2.google.com/service/update2/crx", "update_url": "https://clients2.google.com/service/update2/crx",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB" "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB"
} }

View File

@ -51,7 +51,6 @@ _number_ - maximum stack trace frames to be stored (in case `trace` option was p
_boolean_ or _object_ which contains: _boolean_ or _object_ which contains:
- **options** `object or boolean`: - **options** `object or boolean`:
- `undefined` - will use regular `JSON.stringify` to send data (it's the fast mode). - `undefined` - will use regular `JSON.stringify` to send data (it's the fast mode).
- `false` - will handle also circular references. - `false` - will handle also circular references.
- `true` - will handle also date, regex, undefined, primitives, error objects, symbols, maps, sets and functions. - `true` - will handle also date, regex, undefined, primitives, error objects, symbols, maps, sets and functions.

View File

@ -1,28 +1,22 @@
{ {
"version": "3.1.10", "version": "3.2.10",
"name": "Redux DevTools", "name": "Redux DevTools",
"description": "Redux DevTools for debugging application's state changes.", "description": "Redux DevTools for debugging application's state changes.",
"homepage_url": "https://github.com/reduxjs/redux-devtools", "homepage_url": "https://github.com/reduxjs/redux-devtools",
"manifest_version": 2, "manifest_version": 3,
"page_action": { "action": {
"default_icon": "img/logo/gray.png", "default_icon": "img/logo/gray.png",
"default_title": "Redux DevTools", "default_title": "Redux DevTools",
"default_popup": "window.html#popup" "default_popup": "devpanel.html#popup"
}, },
"commands": { "commands": {
"devtools-left": { "devtools-window": {
"description": "DevTools window to left" "description": "DevTools window"
},
"devtools-right": {
"description": "DevTools window to right"
},
"devtools-bottom": {
"description": "DevTools window to bottom"
}, },
"devtools-remote": { "devtools-remote": {
"description": "Remote DevTools" "description": "Remote DevTools"
}, },
"_execute_page_action": { "_execute_action": {
"suggested_key": { "suggested_key": {
"default": "Ctrl+Shift+E" "default": "Ctrl+Shift+E"
} }
@ -34,34 +28,35 @@
"128": "img/logo/128x128.png" "128": "img/logo/128x128.png"
}, },
"options_ui": { "options_ui": {
"page": "options.html", "page": "options.html"
"chrome_style": true
}, },
"background": { "background": {
"scripts": ["background.bundle.js"], "service_worker": "background.bundle.js"
"persistent": false
}, },
"content_scripts": [ "content_scripts": [
{ {
"matches": ["<all_urls>"], "matches": ["<all_urls>"],
"exclude_globs": ["https://www.google*"], "exclude_globs": ["https://www.google*"],
"js": ["content.bundle.js", "pagewrap.bundle.js"], "js": ["content.bundle.js"],
"run_at": "document_start", "run_at": "document_start",
"all_frames": true "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", "devtools_page": "devtools.html",
"web_accessible_resources": ["page.bundle.js"],
"externally_connectable": { "externally_connectable": {
"ids": ["*"] "ids": ["*"]
}, },
"permissions": [ "permissions": ["notifications", "contextMenus", "storage"],
"notifications", "host_permissions": ["file:///*", "http://*/*", "https://*/*"],
"contextMenus", "content_security_policy": {
"storage", "extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
"file:///*", }
"http://*/*",
"https://*/*"
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
} }

View File

@ -0,0 +1,39 @@
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,
},
},
},
];

View File

@ -1,36 +0,0 @@
/**
* 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');
}
}
}

View File

@ -1,3 +0,0 @@
{
"presets": ["es2015", "stage-0", "react"]
}

View File

@ -1,37 +0,0 @@
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);
};
}

View File

@ -1,27 +0,0 @@
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;

View File

@ -1,16 +0,0 @@
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);

View File

@ -1,10 +0,0 @@
<!doctype html>
<html>
<head>
<title>Redux counter example</title>
</head>
<body>
<div id="root"></div>
<script src="/static/bundle.js"></script>
</body>
</html>

View File

@ -1,14 +0,0 @@
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'),
);

View File

@ -1,41 +0,0 @@
{
"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"
}
}

View File

@ -1,12 +0,0 @@
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;
}
}

View File

@ -1,8 +0,0 @@
import { combineReducers } from 'redux';
import counter from './counter';
const rootReducer = combineReducers({
counter,
});
export default rootReducer;

View File

@ -1,32 +0,0 @@
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,
);
}
});

View File

@ -1,28 +0,0 @@
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;
}

View File

@ -1,73 +0,0 @@
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));
});
});

View File

@ -1,53 +0,0 @@
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();
});
});

View File

@ -1,53 +0,0 @@
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/);
});
});
});

View File

@ -1,23 +0,0 @@
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);
});
});
});

View File

@ -1,5 +0,0 @@
import { jsdom } from 'jsdom';
global.document = jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;

View File

@ -1,23 +0,0 @@
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/,
},
],
},
};

View File

@ -1,3 +0,0 @@
{
"presets": ["es2015", "stage-0", "react"]
}

View File

@ -1,62 +0,0 @@
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;

View File

@ -1,10 +0,0 @@
<!doctype html>
<html>
<head>
<title>React counter example</title>
</head>
<body>
<div id="root"></div>
<script src="/static/bundle.js"></script>
</body>
</html>

View File

@ -1,5 +0,0 @@
import React from 'react';
import { render } from 'react-dom';
import Counter from './components/Counter';
render(<Counter />, document.getElementById('root'));

View File

@ -1,33 +0,0 @@
{
"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"
}
}

View File

@ -1,25 +0,0 @@
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,
},
};

View File

@ -1,4 +0,0 @@
{
"presets": ["es2015", "stage-0", "react"],
"plugins": ["add-module-exports", "transform-decorators-legacy"]
}

View File

@ -1,25 +0,0 @@
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 };
}

View File

@ -1,76 +0,0 @@
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;

View File

@ -1,30 +0,0 @@
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;

View File

@ -1,94 +0,0 @@
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;

View File

@ -1,75 +0,0 @@
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;

View File

@ -1,59 +0,0 @@
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;

View File

@ -1,6 +0,0 @@
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';

View File

@ -1,3 +0,0 @@
export const SHOW_ALL = 'show_all';
export const SHOW_COMPLETED = 'show_completed';
export const SHOW_ACTIVE = 'show_active';

View File

@ -1,38 +0,0 @@
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);

View File

@ -1,21 +0,0 @@
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;

View File

@ -1,68 +0,0 @@
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;

View File

@ -1,10 +0,0 @@
<!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>

View File

@ -1,16 +0,0 @@
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'),
);

View File

@ -1,53 +0,0 @@
{
"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"
}
}

View File

@ -1,10 +0,0 @@
import { combineReducers } from 'redux';
import { routerStateReducer } from 'redux-router';
import todos from './todos';
const rootReducer = combineReducers({
todos,
router: routerStateReducer,
});
export default rootReducer;

View File

@ -1,61 +0,0 @@
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;
}
}

View File

@ -1,32 +0,0 @@
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,
);
}
});

View File

@ -1,28 +0,0 @@
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;
}

View File

@ -1,46 +0,0 @@
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,
});
});
});

View File

@ -1,108 +0,0 @@
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();
});
});
});

View File

@ -1,50 +0,0 @@
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);
});
});
});

View File

@ -1,150 +0,0 @@
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]);
});
});
});
});

View File

@ -1,118 +0,0 @@
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('');
});
});
});

View File

@ -1,84 +0,0 @@
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);
});
});
});

View File

@ -1,325 +0,0 @@
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,
},
]);
});
});

View File

@ -1,5 +0,0 @@
import { jsdom } from 'jsdom';
global.document = jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;

View File

@ -1,32 +0,0 @@
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,
},
],
},
};

View File

@ -1,3 +0,0 @@
{
"presets": ["es2015", "stage-0", "react"]
}

View File

@ -1,13 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Redux Saga Counter example</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/static/bundle.js"></script>
</body>
</html>

View File

@ -1,37 +0,0 @@
{
"name": "redux-counter-example",
"version": "0.0.0",
"description": "Redux counter example",
"scripts": {
"start": "webpack-dev-server --progress"
},
"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.0.0",
"react-dom": "^16.0.0",
"react-redux": "^6.0.0",
"redux": "^4.0.0",
"redux-saga": "^0.10.5"
},
"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.1.0",
"webpack-hot-middleware": "^2.24.0"
}
}

View File

@ -1,27 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const Counter = ({
value,
onIncrement,
onIncrementAsync,
onDecrement,
onIncrementIfOdd,
}) => (
<p>
Clicked: {value} times <button onClick={onIncrement}>+</button>{' '}
<button onClick={onDecrement}>-</button>{' '}
<button onClick={onIncrementIfOdd}>Increment if odd</button>{' '}
<button onClick={onIncrementAsync}>Increment async</button>
</p>
);
Counter.propTypes = {
value: PropTypes.number.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired,
onIncrementAsync: PropTypes.func.isRequired,
onIncrementIfOdd: PropTypes.func.isRequired,
};
export default Counter;

View File

@ -1,43 +0,0 @@
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
// import sagaMonitor from './sagaMonitor'
import Counter from './components/Counter';
import reducer from './reducers';
import rootSaga from './sagas';
const sagaMiddleware = createSagaMiddleware(/* {sagaMonitor} */);
const composeEnhancers =
(window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
trace: true,
traceLimit: 25,
})) ||
compose;
const store = createStore(
reducer,
composeEnhancers(applyMiddleware(sagaMiddleware)),
);
sagaMiddleware.run(rootSaga);
const action = (type) => store.dispatch({ type });
function render() {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
onIncrementAsync={() => action('INCREMENT_ASYNC')}
/>,
document.getElementById('root'),
);
}
render();
store.subscribe(render);

View File

@ -1,12 +0,0 @@
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'INCREMENT_IF_ODD':
return state % 2 !== 0 ? state + 1 : state;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}

View File

@ -1,14 +0,0 @@
/* eslint-disable no-constant-condition */
import { takeEvery } from 'redux-saga';
import { put, call } from 'redux-saga/effects';
import { delay } from 'redux-saga';
export function* incrementAsync() {
yield call(delay, 1000);
yield put({ type: 'INCREMENT' });
}
export default function* rootSaga() {
yield* takeEvery('INCREMENT_ASYNC', incrementAsync);
}

View File

@ -1,25 +0,0 @@
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: [path.join(__dirname, 'src', 'main')],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/',
},
module: {
rules: [
{
test: /\.js$/,
loaders: ['babel-loader'],
exclude: /node_modules/,
},
],
},
devServer: {
port: 4003,
},
};

View File

@ -1,3 +0,0 @@
{
"presets": ["es2015", "stage-0", "react"]
}

View File

@ -1 +0,0 @@
export * from './todos';

View File

@ -1,25 +0,0 @@
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 };
}

View File

@ -1,77 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
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;

View File

@ -1,30 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TodoTextInput from './TodoTextInput';
class Header extends Component {
handleSave(text) {
if (text.length !== 0) {
this.props.addTodo(text);
}
}
render() {
return (
<header className="header">
<h1>todos</h1>
<TodoTextInput
newTodo
onSave={this.handleSave.bind(this)}
placeholder="What needs to be done?"
/>
</header>
);
}
}
Header.propTypes = {
addTodo: PropTypes.func.isRequired,
};
export default Header;

View File

@ -1,95 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
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;

View File

@ -1,76 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
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;

View File

@ -1,60 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
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;

View File

@ -1,6 +0,0 @@
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';

View File

@ -1,3 +0,0 @@
export const SHOW_ALL = 'show_all';
export const SHOW_COMPLETED = 'show_completed';
export const SHOW_ACTIVE = 'show_active';

View File

@ -1,38 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
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, actions } = this.props;
return (
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
);
}
}
App.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired,
};
function mapStateToProps(state) {
return {
todos: state.todos,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(TodoActions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);

View File

@ -1,10 +0,0 @@
<!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>

View File

@ -1,16 +0,0 @@
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import configureStore from './store/configureStore';
import 'todomvc-app-css/index.css';
const store = configureStore();
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);

View File

@ -1,42 +0,0 @@
{
"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",
"prop-types": "^15.6.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-redux": "^6.0.0",
"redux": "^4.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",
"express": "^4.13.3",
"raw-loader": "^1.0.0",
"style-loader": "^0.23.0",
"todomvc-app-css": "^2.0.1",
"webpack": "^4.0.0",
"webpack-dev-server": "^3.0.0",
"webpack-hot-middleware": "^2.2.0"
}
}

View File

@ -1,8 +0,0 @@
import { combineReducers } from 'redux';
import todos from './todos';
const rootReducer = combineReducers({
todos,
});
export default rootReducer;

View File

@ -1,67 +0,0 @@
import {
ADD_TODO,
DELETE_TODO,
EDIT_TODO,
COMPLETE_TODO,
COMPLETE_ALL,
CLEAR_COMPLETED,
} from '../constants/ActionTypes';
const initialState = [
{
text: 'Use Redux',
completed: false,
modified: new Date(),
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,
modified: new Date(),
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, modified: new Date() })
: todo,
);
case COMPLETE_TODO:
return state.map((todo) =>
todo.id === action.id
? Object.assign({}, todo, {
completed: !todo.completed,
modified: new Date(),
})
: todo,
);
case COMPLETE_ALL:
const areAllMarked = state.every((todo) => todo.completed);
return state.map((todo) =>
Object.assign({}, todo, {
completed: !areAllMarked,
modified: new Date(),
}),
);
case CLEAR_COMPLETED:
return state.filter((todo) => todo.completed === false);
default:
return state;
}
}

View File

@ -1,32 +0,0 @@
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,
);
}
});

View File

@ -1,30 +0,0 @@
import { createStore } from 'redux';
import rootReducer from '../reducers';
import * as actionCreators from '../actions';
export default function configureStore(preloadedState) {
const enhancer =
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__({
actionCreators,
serialize: true,
trace: true,
});
if (!enhancer) {
console.warn(
'Install Redux DevTools Extension to inspect the app state: ' +
'https://github.com/zalmoxisus/redux-devtools-extension#installation',
);
}
const store = createStore(rootReducer, preloadedState, enhancer);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
store.replaceReducer(require('../reducers').default);
});
}
return store;
}

View File

@ -1,46 +0,0 @@
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,
});
});
});

View File

@ -1,108 +0,0 @@
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();
});
});
});

View File

@ -1,50 +0,0 @@
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);
});
});
});

View File

@ -1,150 +0,0 @@
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]);
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More