diff --git a/.dockerignore b/.dockerignore
index 9d73121d0ec..c275de2d4ec 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -49,7 +49,6 @@ vendor/ruby
package-lock.json
npm-debug.log
.vscode
-.yo-rc.json
out*
.byebug_history
.gitignore
diff --git a/.eslintignore b/.eslintignore
index 276294dd234..b0d3fec0480 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -5,5 +5,4 @@ config/webpack.config.js
webpack/simple_named_modules.js
*.test.js
*fixture*
-*.stories.js
*const*
diff --git a/.github/workflows/storybook_deploy_main.yml b/.github/workflows/storybook_deploy_main.yml
deleted file mode 100644
index c195ba00e36..00000000000
--- a/.github/workflows/storybook_deploy_main.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: Deploy Main Storybook
-
-on:
- push:
- branches:
- - develop
- paths:
- - 'webpack/**'
-
-permissions:
- contents: read
-
-jobs:
- deploy_main_storybook:
-
- runs-on: ubuntu-latest
- if: github.repository == 'theforeman/foreman'
-
- steps:
- - uses: actions/checkout@v3
- - uses: ruby/setup-ruby@v1
- with:
- ruby-version: 2.7
- - name: Run npm install
- run: npm install
- - name: Build Storybook
- run: npm run build-storybook -- --output-dir=storybooks/main
- - name: Deploy Storybook to surge.sh
- env:
- SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}
- run: |
- npm install --save-dev surge
- npx surge --project storybooks/main --domain ${{ secrets.SURGE_DOMAIN }}
- id: surge_deploy
diff --git a/.gitignore b/.gitignore
index 258b302f1cd..894cd62e741 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,7 +50,6 @@ public/webpack
package-lock.json
npm-debug.log
.vscode
-.yo-rc.json
.vendor/
.solargraph.yml
.nvmrc
diff --git a/README.md b/README.md
index 37cb7bef110..b5e3369f42e 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,6 @@
[](https://codeclimate.com/github/theforeman/foreman)
[](https://coveralls.io/github/theforeman/foreman?branch=develop)
[](https://kiwiirc.com/client/irc.libera.chat/?#theforeman)
-[](https://foreman-storybook.surge.sh)
[Foreman](https://theforeman.org) is a free open source project that gives you the power to easily **automate repetitive tasks**, quickly **deploy applications**, and proactively **manage your servers life cycle**, on-premises or in the cloud.
diff --git a/webpack/stories/docs/adding-dependencies.stories.mdx b/developer_docs/adding-dependencies.asciidoc
similarity index 65%
rename from webpack/stories/docs/adding-dependencies.stories.mdx
rename to developer_docs/adding-dependencies.asciidoc
index 42244636585..9cbaeea321b 100644
--- a/webpack/stories/docs/adding-dependencies.stories.mdx
+++ b/developer_docs/adding-dependencies.asciidoc
@@ -1,22 +1,19 @@
-import { Meta } from '@theforeman/stories';
+[[adding-js-dependencies]]
+= NPM dependencies
+:toc: right
+:toclevels: 5
-
-
-# Using/Adding/updating NPM dependencies
+## Using/Adding/updating NPM dependencies
Foreman manage npm dependencies with a seperate project called `@theforeman/vendor` which responsible to deliver 3rd-party modules to foreman and its plugins.
Foreman and its plugins consumes `@theforeman/vendor` project from `npm` in development and from `rpm` in production.
-`@theforeman/vendor` lives inside a monorepo together with other foreman javascript tools in a project called [`foreman-js`](https://github.com/theforeman/foreman-js)
+`@theforeman/vendor` lives inside a monorepo together with other foreman javascript tools in a project called https://github.com/theforeman/foreman-js[foreman-js]
+
-[Read more about `@theforeman/vendor`](https://github.com/theforeman/foreman-js/tree/master/packages/vendor)
+https://github.com/theforeman/foreman-js/tree/master/packages/vendor[Read more about @theforeman/vendor]
-### Consuming `foreman-js` projects from source (locally)
+## Consuming `foreman-js` projects from source (locally)
Clone, install, build and link the `foreman-js` project to foreman:
diff --git a/developer_docs/adding-new-components.asciidoc b/developer_docs/adding-new-components.asciidoc
new file mode 100644
index 00000000000..3470389e802
--- /dev/null
+++ b/developer_docs/adding-new-components.asciidoc
@@ -0,0 +1,116 @@
+
+[[adding-new-react-components]]
+
+# Adding new React components
+
+## Component Storage
+
+Components are stored in the webpack/assets/javascripts/react_app/components/ directory. Each component should be placed in its own subfolder that follows the structure outlined below:
+
+```
+─ components//
+ ├─ .js ┈ react component
+ ├─ .scss ┈ styles if needed
+ ├─ .fixtures.js ┈ constants for testing, initial state, etc.
+ ├─ .test.js ┈ test file for the component
+ ├─ components/ ┈ folder for nested components if needed
+```
+
+React coponent files are limited to 100 lines of code. If you need to write more code, consider splitting the component into multiple components and/or wrapping the react component with an index file that will preform any general logic, api calls.
+
+If you are creating a component that uses legacy Redux actions and reducers, the structure should be as follows:
+
+```
+─ components//
+ ├─ .js ┈ pure react component
+ ├─ .scss ┈ styles if needed
+ ├─ Actions.js ┈ redux actions
+ ├─ Reducer.js ┈ redux reducer
+ ├─ Selectors.js ┈ reselect selectors
+ ├─ Constants.js ┈ constants such as action types
+ ├─ .fixtures.js ┈ constants for testing, initial state, etc.
+ ├─ components/ ┈ folder for nested components if needed
+ ├─ __tests__/ ┈ folder for tests
+ ╰─ index.js ┈ redux connected file
+```
+
+## Testing
+
+### Testing components
+
+Please use React-Testing-Library for tests. It's a library that helps you test your components in a way that resembles how they are used by the end user. It's a good idea to read the https://testing-library.com/docs/guiding-principles[guiding principles] of the library.
+
+### Running the tests
+
+All tests can be executed with `npm test`.
+
+If you want to run only a single test run `npm test \-- `. For example `npm test \-- BreadcrumbBar.test.js`.
+
+Linter (code style checking) can be executed with `npm run lint`. You can run it with parameter `--fix` to let it automatically fix the discovered issues. You need to pass the parameter to eslint, so run the command like this `npm run lint \-- --fix`.
+
+## Making it available from ERB
+
+If you want your component to be available for mounting into ERB templates, it must be added to [the component registry](https://github.com/theforeman/foreman/blob/develop/webpack/assets/javascripts/react_app/components/componentRegistry.js#L60-L71).
+
+Then, you can mount it with the `react_component` helper:
+
+```ruby
+react_component(component_name, props)
+```
+
+**Example:**
+
+```erb
+
+```
+
+will render the following HTML:
+
+```html
+
+
+
+```
+
+(Note that the React component is rendered as a [web component](https://developer.mozilla.org/en-US/docs/Web/Web_Components).)
+
+### Changing the props from legacy JS
+
+We allow changing the root component props from the legacy JS.
+Be aware, that this will re-render the component.
+This feature should only be used for limited use cases:
+
+1. A legacy JS library/component needs to talk to a React component, AND
+1. The component is simple enough that it wouldn't otherwise make sense to store its data in the Redux store.
+
+We will use a method `reactProps` that our web component exposes and get the current props.
+Then we will change this props and use `reactProps=` setter that will trigger the rerender.
+
+
+```js
+var myCoolPowerElement = document.getElementById("my-cool-power-status").getElementsByTagName('foreman-react-component')[0];
+var newProps = myCoolPowerElement.reactProps;
+
+newProps.errorText = 'MyNewErrorText';
+myCoolPowerElement.reactProps = newProps;
+
+```
+
+
+*Note* that you can also directly set the data: `element.dataset.props = JSON.stringify({new: 'prop'});`
+or even the attribute: `element.setAttribute('data-props', JSON.stringify({new: 'prop'}));`
+
+Both of these need a JSON string as a new value to work.
+
+## Before you start writing a new component
+
+It's worth checking [PatternFly](https://www.patternfly.org) to make sure such component doesn't exist yet. Also consider if your component is universal enough to be used in other projects. In such case it might make sense to add it to PatternFly instead.
\ No newline at end of file
diff --git a/webpack/stories/docs/api-middleware-usage.stories.mdx b/developer_docs/api-middleware-usage.asciidoc
similarity index 66%
rename from webpack/stories/docs/api-middleware-usage.stories.mdx
rename to developer_docs/api-middleware-usage.asciidoc
index 7b5a4f13de0..c84f38c1052 100644
--- a/webpack/stories/docs/api-middleware-usage.stories.mdx
+++ b/developer_docs/api-middleware-usage.asciidoc
@@ -1,13 +1,14 @@
-import { Meta } from '@theforeman/stories';
+[[api-middleware-intro]]
-
+= API Middleware Usage
+:toc: right
+:toclevels: 5
-# How to use API in a Component using useAPI hook
+# API Middleware
+
+Instead of each component handling API calls in the same way we have the API Middleware that will handle it.
+
+## How to use API in a Component using useAPI hook
The API middleware is abstracted by the `useAPI` custom hook.
@@ -19,7 +20,7 @@ import { successCallback, errorCallback } from './helper';
const MyComponent = () => {
const options = {
handleSuccess: successCallback,
- handleError: errorCallback,
+ handleError: error => (error.response.status === 401 ? logoutUser() : null),
successToast: response => 'This text will be shown as a toast after a success call',
errorToast: response => 'This text will be shown as a toast when error occurs',
};
@@ -27,31 +28,29 @@ const MyComponent = () => {
response: { results },
status, // The current status of the API call
key, // Generated key for storing in redux's store
- } = useAPI('get', '/api/audits', options);
+ setAPIOptions, // Function to update the options and make a new api call
+ } = useAPI('get', '/api/hosts', options);
return (
- {audits.map(item => (
-
- {item.title} {item.action}
-
+
+
+ {results.map(item => (
+
{item.title}
))}
);
};
```
-# Example: how to use the API middleware
+## How to use the API middleware
-The example API returns a JSON object like this:
+The api middleware is a redux middleware that handles API calls in the application.
+It is recommended to use the `useAPI` hook instead of using the middleware directly.
-```json
-{
- "items": [
- { "id": 319, "title": "setting", "action": "update" },
- { "id": 150, "title": "bookmark", "action": "create" }
- ]
-}
-```
```js
/** MyComponent.js*/
@@ -113,6 +112,10 @@ ConnectedMyComponent.propTypes = {
export default ConnectedMyComponent;
```
+### Access the API store
+
+We provided you the `selectAPIByKey` in '/APISelectors.js' which will return the key substate,
+there are also `selectAPIStatus`, `selectAPIPayload`, `selectAPIResponse`, `selectAPIError` and `selectAPIErrorMessage`.
```js
/** MyComponentSelectors.js*/
@@ -132,6 +135,15 @@ export const selectStatus = state => selectAPIStatus(state, MY_SPECIAL_KEY);
export const selectError = state => selectAPIError(state, MY_SPECIAL_KEY);
```
+Then there will be called 2 actions: **MY_SPECIAL_KEY_REQUEST** and **MY_SPECIAL_KEY_SUCCESS/ MY_SPECIAL_KEY_FAILURE**:
+**MY_SPECIAL_KEY_REQUEST** will have the payload only
+**MY_SPECIAL_KEY_SUCCESS** will have the payload and the return data from the API call.
+**MY_SPECIAL_KEY_FAILURE** will have the payload and the return error from the API call.
+
+In the **payload** field you should send any headers and params for the GET request, and any other data you want for the action.
+
+The actions types can be changed with the optional **actionTypes** parameter:
+
```js
/** MyComponentActions.js*/
@@ -145,10 +157,23 @@ export const getData = url => ({
page: 2,
per_page: 10,
},
+ actionTypes: {
+ REQUEST: 'CUSTOM_REQUEST',
+ }
},
});
```
+The example API returns a JSON object like this:
+
+```json
+{
+ "items": [
+ { "id": 319, "title": "setting", "action": "update" },
+ { "id": 150, "title": "bookmark", "action": "create" }
+ ]
+}
+```
Once the action is triggered, the API middleware will manage the request
and update the store with the request status:
diff --git a/webpack/stories/docs/client-routing.stories.mdx b/developer_docs/client-routing.asciidoc
similarity index 86%
rename from webpack/stories/docs/client-routing.stories.mdx
rename to developer_docs/client-routing.asciidoc
index 54c35c76641..80ad58ad7d9 100644
--- a/webpack/stories/docs/client-routing.stories.mdx
+++ b/developer_docs/client-routing.asciidoc
@@ -1,40 +1,39 @@
-import { Meta } from '@theforeman/stories';
-
-
-
+[[client-routing]]
# Client Routing
-Foreman uses `react-router` for rendering react pages without full page reload,
+:toc: right
+:toclevels: 5
+Foreman uses `react-router` for rendering react pages without full page reload.
## Core
In order to add a new route in foreman core, please follow these steps:
1. Create a folder under `/react_app/routes` directory
2. Create an index file and import the wanted component:
-
-```js
-import React from 'react';
-import Page1 from './page1';
-import { PAGE1_URL } from './constants';
-
-export default {
- path: PAGE1_URL,
- render: props => ,
-};
-```
++
+[source,js]
+----
+ import React from 'react';
+ import Page1 from './page1';
+ import { PAGE1_URL } from './constants';
+
+ export default {
+ path: PAGE1_URL,
+ render: props => ,
+ };
+----
++
3. import that index in `routes.js` file:
-```js
++
+[source,js]
+----
// Other routes...
import Page1 from './Page1';
import Page2 from './Page2'
export const routes = [Page1, Page2];
-```
+----
++
4. Add a route that point to this page in `routes.rb` :
```ruby
match 'page1' => 'react#index', :via => :get
diff --git a/webpack/stories/docs/fetching-data-with-graphql.stories.mdx b/developer_docs/fetching-data-with-graphql.asciidoc
similarity index 90%
rename from webpack/stories/docs/fetching-data-with-graphql.stories.mdx
rename to developer_docs/fetching-data-with-graphql.asciidoc
index 98ca76c4c18..f81acfc8971 100644
--- a/webpack/stories/docs/fetching-data-with-graphql.stories.mdx
+++ b/developer_docs/fetching-data-with-graphql.asciidoc
@@ -1,12 +1,4 @@
-import { Meta } from '@theforeman/stories';
-
-
-
+[[js-fetching-data-with-graphql]]
# Fetching data with GraphQL
Foreman uses Apollo client to talk to GraphQL endpoint. All the components within react routes have access to the Apollo client as routes are wrapped with ApolloProvider (see ReactApp component for details). Queries can be executed simply by using a hook:
diff --git a/webpack/stories/docs/foreman-context.stories.mdx b/developer_docs/foreman-context.asciidoc
similarity index 85%
rename from webpack/stories/docs/foreman-context.stories.mdx
rename to developer_docs/foreman-context.asciidoc
index 56c8a195bae..3c3851282bc 100644
--- a/webpack/stories/docs/foreman-context.stories.mdx
+++ b/developer_docs/foreman-context.asciidoc
@@ -1,13 +1,7 @@
-import { Meta } from '@theforeman/stories';
-
-
-
+[[foreman-context]]
# ForemanContext
+:toc: right
+:toclevels: 5
ForemanContext is an implementation of the new `React Context` API.
Global metadata (such as version, pagination, user, taxonomies, theme, foreman's settings, etc...) can be shared over all react nodes
@@ -17,9 +11,8 @@ without any redux integration nor API request.
`Foreman Context` comes with every react component by default, like redux's store.
-### How to read a value
+### Reading values from the context
-#### With Hooks
Like selectors, you can consume context values by custom hooks:
```js
@@ -29,7 +22,8 @@ import {
useForemanVersion,
useForemanOrganization,
useForemanLocation,
- useForemanUser
+ useForemanUser,
+ useForemanDocUrl,
} from '../../Root/Context/ForemanContext';
const { perPage } = useForemanSettings();
@@ -39,7 +33,7 @@ const { id, title } = useForemanOrganization();
const { id, login, firstname, lastname, admin } = useForemanUser();
```
-### How to add a value to the context
+## How to add a value to the context
Keys and values are set in the `app_metadata` method of `/application_helper.rb`.
The `app_metadata` object is passed down and becomes ForemanContext.
diff --git a/webpack/stories/docs/getting-started.stories.mdx b/developer_docs/getting-started.asciidoc
similarity index 71%
rename from webpack/stories/docs/getting-started.stories.mdx
rename to developer_docs/getting-started.asciidoc
index 5d4a77820b4..843502df315 100644
--- a/webpack/stories/docs/getting-started.stories.mdx
+++ b/developer_docs/getting-started.asciidoc
@@ -1,13 +1,8 @@
-import { Meta } from '@theforeman/stories';
+[[js-getting-started]]
-
-
-# Getting started
+# Getting started with frontend development
+:toc: right
+:toclevels: 5
## Development setup
@@ -15,19 +10,20 @@ Following steps are required to setup a webpack development environment:
1. **Settings**
There are 2 relevant settings in `config/settings.yml`. At least `webpack_dev_server` should be set to true:
-
- ```yaml
- # Use the webpack development server?
- # Should be set to true if you want to conveniently develop webpack-processed code.
- # Make sure to run `rake webpack:compile` if disabled.
- :webpack_dev_server: true
-
- # If you run Foreman in development behind some proxy or use HTTPS you need
- # to enable HTTPS for webpack dev server too, otherwise you'd get mixed content
- # errors in your browser
- :webpack_dev_server_https: true
- ```
-
++
+[source,yaml]
+----
+# Use the webpack development server?
+# Should be set to true if you want to conveniently develop webpack-processed code.
+# Make sure to run `rake webpack:compile` if disabled.
+:webpack_dev_server: true
+
+# If you run Foreman in development behind some proxy or use HTTPS you need
+# to enable HTTPS for webpack dev server too, otherwise you'd get mixed content
+# errors in your browser
+:webpack_dev_server_https: true
+----
++
2. **Dependencies**
Make sure you have all npm dependencies up to date:
`npm install`
@@ -48,10 +44,10 @@ Following steps are required to setup a webpack development environment:
4. **Additional config**
Both `foreman start` and `foreman-start-dev` support `WEBPACK_OPTS` environment variable for passing additional options. This is handy for example when you have development setup with Katello and want to use correct certificates.
-
An example of such setup:
-
- ```bash
++
+[source,bash]
+----
./node_modules/.bin/webpack-dev-server \
--config config/webpack.config.js \
--port 3808 \
@@ -61,18 +57,23 @@ Following steps are required to setup a webpack development environment:
--cert /etc/pki/katello/certs/katello-apache.crt \
--cacert /etc/pki/katello/certs/katello-default-ca.crt \
--watch-poll 1000 # only use for NFS https://community.theforeman.org/t/webpack-watch-over-nfs/10922
- ```
-
- Additionally you can set `NOTIFICATIONS_POLLING` variable to extend the notification polling interval that is 10s by default and can clutter the console.
-
- ```bash
+----
++
+Additionally you can set `NOTIFICATIONS_POLLING` variable to extend the notification polling interval that is 10s by default and can clutter the console.
++
+[source,bash]
+----
NOTIFICATIONS_POLLING=${polling_interval_in_ms}
- ```
- Webpack stats ([docs](https://webpack.js.org/configuration/stats/)) can be changed by `WEBPACK_STATS`. Default value is `minimal`.
- ```bash
+----
++
+Webpack stats can be changed by `WEBPACK_STATS`. Default value is `minimal`.
++
+[source,bash]
+----
WEBPACK_STATS=${verbose}
- ```
+
+----
## Directory structure
@@ -81,13 +82,12 @@ The webpack processed code is placed in the following folder structure:
```
─ webpack/ ┈ all webpack processed code
│
- ├─ stories/ ┈ storybook config (this pages)
╰─ assets/javascripts/ ┈ es6 code for erb pages, some still contain jQuery
│
╰─ react_app/ ┈ react components and related code
```
-More detailed description of a folder structure for components is in chapter [Adding new component](./?selectedKind=Introduction&selectedStory=Adding%20new%20component).
+More detailed description of a folder structure for components is in chapter https://github.com/theforeman/foreman/blob/develop/developer_docs/adding-new-components.asciidoc[Adding new component].
There are still obsolete `redux` folders at some places. They used to be a place for files containing Redux actions and reducers before a standardized folder structure was introduced. We're migrating away from them. Please don't put additional code there.
## Useful tools
diff --git a/webpack/stories/docs/hoc.stories.mdx b/developer_docs/hoc.asciidoc
similarity index 60%
rename from webpack/stories/docs/hoc.stories.mdx
rename to developer_docs/hoc.asciidoc
index d24ba364c06..267f9efdaa3 100644
--- a/webpack/stories/docs/hoc.stories.mdx
+++ b/developer_docs/hoc.asciidoc
@@ -1,13 +1,8 @@
-import { Meta } from '@theforeman/stories';
+[[react-hoc]]
-
+# Legacy Higher Order Components
-# Higher Order Components
+*Foremans `withRenderHandler` is not maintained anymore, and should not be used in new code.*
A higher-order component (HOC) is an advanced technique in React for reusing component logic. We have created several HOCs which makes it easier to write, read and maintain new code.
@@ -25,13 +20,14 @@ the following props are also required:
Here we can see all the optional states and their chosen view.
-| isLoading | hasData | hasError | VIEW | Comments |
-| --------- | ------- | -------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
-| TRUE💚 | FALSE🔴 | FALSE🔴 | **Loading** | Initial Loading **OR** after Message |
-| FALSE🔴 | TRUE💚 | FALSE🔴 | **Component** | Show Data |
-| TRUE💚 | TRUE💚 | FALSE🔴 | **Component** | Outdated data exists, should not show loading to avoid flickering so we still show the component with the outdated data until the new data arrives |
-| FALSE🔴 | FALSE🔴 | FALSE🔴 | **Empty** | query returned 0 results, this is not an error, and we display a message |
-| FALSE🔴 | FALSE🔴 | TRUE💚 | **Error** | query failed, and we display a message |
+|===
+| isLoading | hasData | hasError | VIEW | Comments
+| 💚TRUE| FALSE🔴 | FALSE🔴 | **Loading** | Initial Loading **OR** after Message
+| 🔴FALSE | TRUE💚 | FALSE🔴 | **Component** | Show Data
+| 💚TRUE | TRUE💚 | FALSE🔴 | **Component** | Outdated data exists, should not show loading to avoid flickering so we still show the component with the outdated data until the new data arrives
+| 🔴FALSE | FALSE🔴 | FALSE🔴 | **Empty** | query returned 0 results, this is not an error, and we display a message
+| 🔴FALSE | FALSE🔴 | TRUE💚 | **Error** | query failed, and we display a message
+|===
### Example
diff --git a/webpack/stories/docs/images/foreman-frontend-infra.png b/developer_docs/images/foreman-frontend-infra.png
similarity index 100%
rename from webpack/stories/docs/images/foreman-frontend-infra.png
rename to developer_docs/images/foreman-frontend-infra.png
diff --git a/webpack/stories/docs/interval-middleware.stories.mdx b/developer_docs/interval-middleware.asciidoc
similarity index 95%
rename from webpack/stories/docs/interval-middleware.stories.mdx
rename to developer_docs/interval-middleware.asciidoc
index da8c607b3eb..c96e27aebeb 100644
--- a/webpack/stories/docs/interval-middleware.stories.mdx
+++ b/developer_docs/interval-middleware.asciidoc
@@ -1,13 +1,6 @@
-import { Meta } from '@theforeman/stories';
-
-
-
# Interval Middleware
+:toc: right
+:toclevels: 5
This middleware will run an action for every given interval miliseconds,
and will manage all running intervals in the Redux store so we could clear the interval later.
diff --git a/webpack/stories/docs/legacy-js.stories.mdx b/developer_docs/legacy-js.asciidoc
similarity index 69%
rename from webpack/stories/docs/legacy-js.stories.mdx
rename to developer_docs/legacy-js.asciidoc
index 576579e9c7a..71657ec2b2c 100644
--- a/webpack/stories/docs/legacy-js.stories.mdx
+++ b/developer_docs/legacy-js.asciidoc
@@ -1,30 +1,23 @@
-import { Meta } from '@theforeman/stories';
-import ForemanFrontendDiagram from './images/foreman-frontend-infra.png';
-
-
-
+[[legacy-js]]
# Legacy JS
+:toc: right
+:toclevels: 5
Foreman's legacy javascript is based on ruby on rails assets pipeline and located in `assets/javascripts`
New features are most welcome on Webpack, but you can still get data directly from Redux's state and observe the state within the legacy JS.
-> ⚠️ **Warning**: Do not update react content with jquery or other DOM manipulation
-> ⚠️ **Warning**: Do not alter the state manually, only via actions
-> ⚠️ **Warning**: Avoid changing the actual DOM with react
+- ⚠️ **Warning**: Do not update react content with jquery or other DOM manipulation
+- ⚠️ **Warning**: Do not alter the state manually, only via actions
+- ⚠️ **Warning**: Avoid changing the actual DOM with react
-
+image::./images/foreman-frontend-infra.png["Foreman Frontend Infrastructure"]
-### Access webpack's javascript
+## Access webpack's javascript
In order to access new js logic in old js files, we created a global object -`tfm`, which contains a set of functions and located in `/webpack/assets/javascripts/bundle.js`
Please use this object instead of using the `window` object directly.
-### How to invoke an action
+## How to invoke an action
Import the desired action in `/foreman_actions` file
dispatch the action in a legacy js file:
@@ -33,7 +26,7 @@ dispatch the action in a legacy js file:
tfm.store.dispatch('actionName', arg1, arg2);
```
-### Observing the store
+## Observing the store
With `observeStore` you can listen for changes in the store:
diff --git a/webpack/stories/docs/plugins.stories.mdx b/developer_docs/plugins.asciidoc
similarity index 72%
rename from webpack/stories/docs/plugins.stories.mdx
rename to developer_docs/plugins.asciidoc
index 82191b01563..9592168b43f 100644
--- a/webpack/stories/docs/plugins.stories.mdx
+++ b/developer_docs/plugins.asciidoc
@@ -1,18 +1,12 @@
-import { Meta } from '@theforeman/stories';
-
-
-
+[[js-plugins]]
# Plugins
+:toc: right
+:toclevels: 5
## Using components from Foreman core
There are three ways components provided by Foreman core can be re-used:
-
+[[mounting-components-into-erb]]
### 1. Mounting components into ERB
No special setup is required and you can re-use React components that are available in `componentRegistry` even when your plugin doesn't use Webpack.
@@ -42,7 +36,7 @@ will render the following HTML:
(Note that the React component is rendered as a [web component](https://developer.mozilla.org/en-US/docs/Web/Web_Components).)
-The list of available components is [here](https://github.com/theforeman/foreman/blob/develop/webpack/assets/javascripts/react_app/components/componentRegistry.js#L60).
+The list of available components is [here](https://github.com/theforeman/foreman/blob/develop/webpack/assets/javascripts/react_app/components/componentRegistry.js).
### 2. Re-using core code in Webpack
@@ -58,23 +52,7 @@ import { noop } from 'foremanReact/common/helpers';
import { MessageBox } from 'foremanReact/components/common/MessageBox';
```
-### 3. Using components outside of Webpack
-
-The component registry is available in `Window.tfm.componentRegistry`. That gives you access to the components even from js code that isn't processed by Webpack.
-
-```js
-const MyComponent = Window.tfm.componentRegistry.getComponent(componentName)
- .type;
-```
-
-Most of the components must be wrapped with a [Higher-Order Component](https://reactjs.org/docs/higher-order-components.html) that provides some context like Redux store or Intl. `componentRegistry` publishes a wrapper factory that can create a wrapper function with HOCs according to your needs.
-
-```js
-const i18nWrapper = componentRegistry.wrapperFactory().with('i18n').wrapper;
-const MyComponentWithIntl = i18nWrapper(MyComponent);
-```
-
-# Using Webpack in plugins
+## Using Webpack in plugins
There are 3 conditions that a plugin has to fulfill to share the Webpack infrastructure from Foreman core:
@@ -112,6 +90,6 @@ You can make sure Webpack knows about your plugin by executing script `plugin_we
}
}
```
-# How to extend core functionaly
+## How to extend core functionaly
-You can use [Slot&Fill](?selectedKind=Introduction&selectedStory=Slot%26Fill) to extend react components (See Slot&Fill section)
+You can use https://github.com/theforeman/foreman/blob/develop/developer_docs/slot-and-fill.asciidoc[Slot&Fill] to extend react components from core.
\ No newline at end of file
diff --git a/webpack/stories/docs/slot-and-fill.stories.mdx b/developer_docs/slot-and-fill.asciidoc
similarity index 81%
rename from webpack/stories/docs/slot-and-fill.stories.mdx
rename to developer_docs/slot-and-fill.asciidoc
index cfda31007db..b15186d4ad4 100644
--- a/webpack/stories/docs/slot-and-fill.stories.mdx
+++ b/developer_docs/slot-and-fill.asciidoc
@@ -1,26 +1,14 @@
-import { Meta } from '@theforeman/stories';
-
-
-
+[[slot-and-fill]]
# Slot And Fill
+:toc: right
+:toclevels: 5
-Slot & Fill allows plugins to extend foreman core functionality in the UI
+Slot & Fill allows plugins to extend foreman core functionality in the UI.
## Current Slots List
-| Name | Id | Path |
-| -------------------- |:---------------------:| -----:|
-| **About-footer** | aboutFooterSlot | _views/about/index.html.erb_
-| **Hostgroup params** | HostgroupParams | _views/hostgroups/_form.html.erb_
-| **Host params** | HostParams | _views/hosts/_form.html.erb_
-| **Host Registration**| `registrationGeneral` | ``
-| **Host Registration**| `registrationAdvanced`| ``
-
+You can find current slots by search which React component use the `` component.
+You can find .erb pages that use `` component by searching for `slot` helper.
## Components
@@ -52,7 +40,7 @@ _plugin A_
```js
-<\/Fill>
+
```
Plugin B's fill will be render first
@@ -105,7 +93,7 @@ const TextWrapper = ({ text }) =>
{text}
;
-<\/Slot>;
+;
```
_plugin A_
diff --git a/webpack/stories/docs/state-management.stories.mdx b/developer_docs/state-management.asciidoc
similarity index 78%
rename from webpack/stories/docs/state-management.stories.mdx
rename to developer_docs/state-management.asciidoc
index 9f9809319f0..faaa627a94c 100644
--- a/webpack/stories/docs/state-management.stories.mdx
+++ b/developer_docs/state-management.asciidoc
@@ -1,13 +1,7 @@
-import { Meta } from '@theforeman/stories';
-
-
-
+[[state-management]]
# State Management in Foreman & Plugins
+:toc: right
+:toclevels: 5
Where do I put data so that it's accessible by the React components that need it?
@@ -30,7 +24,7 @@ The following sections will outline
This includes anything that uses `useState` or `useReducer` hooks. Or, in class components, `this.state`. See the React docs for more info.
-This is the best place for any data that is _used only locally by the component_: for example
+This is the best place for any data that is _used only locally by the component and its children_: for example
* input value for a controlled UI component
* current form values for form elements
* Other UI element states
@@ -49,20 +43,25 @@ On the other hand, state management is not the only use for Redux. It can also
## ForemanContext
-Data that doesn't often change but can impact many components across the application is stored in a special React Context object called ForemanContext. For more info on how to use it, see [Foreman Context](https://foreman-storybook.surge.sh/?path=/story/introduction-foreman-context--page).
+Data that doesn't often change but can impact many components across the application is stored in a special React Context object called ForemanContext. For more info on how to use it, see https://github.com/theforeman/foreman/blob/develop/developer_docs/foreman-context.asciidoc[Foreman Context].
+
+This data includes:
-This data includes
* Current taxonomy (organization, location)
* Current user
* Pagination settings
* Foreman version
+* UI Settings
+* Documantation url
+
## Global `window` object
Because Foreman and plugins use both modern and legacy JS frameworks, Foreman has a unique way of storing some data on the global `window` object. (See `webpack/assets/javascripts/bundle.js`.) This `tfm` object includes
-* A [`componentRegistry`](https://github.com/theforeman/foreman/blob/develop/webpack/assets/javascripts/react_app/components/componentRegistry.js) that makes React components accessible for mounting on Rails ERB pages. For more info, see [Mounting components into ERB](https://foreman-storybook.surge.sh/?path=/story/introduction-plugins--page#1-mounting-components-into-erb).
-* A method for [legacy JS](https://foreman-storybook.surge.sh/?path=/story/introduction-legacy-js--page) frameworks to access Foreman's Redux store
+* A https://github.com/theforeman/foreman/blob/develop/webpack/assets/javascripts/react_app/components/componentRegistry.js[`componentRegistry`] that makes React components accessible for mounting on Rails ERB pages. For more info, see https://github.com/theforeman/foreman/blob/develop/developer_docs/plugins.asciidoc#mounting-components-into-erb[Mounting components into ERB]
+
+* A method for https://github.com/theforeman/foreman/blob/develop/developer_docs/legacy-js.asciidoc[legacy JS] frameworks to access Foreman's Redux store
* Several other utilities and helpers written in jQuery and vanilla JavaScript
Should be used only if legacy code needs access to such data or methods. Avoid using it if possible.
diff --git a/package-exclude.json b/package-exclude.json
index 818dc95a830..0380ffe09c0 100644
--- a/package-exclude.json
+++ b/package-exclude.json
@@ -3,7 +3,6 @@
"@sheerun/mutationobserver-shim",
"@theforeman/env",
"@theforeman/eslint-plugin-foreman",
- "@theforeman/stories",
"@theforeman/test",
"@theforeman/vendor-dev",
"@theforeman/find-foreman",
@@ -36,7 +35,6 @@
],
"EXCLUDE_NPM_PREFIXES": [
"@babel/eslint-",
- "@storybook/",
"@testing-library/",
"enzyme",
"eslint",
diff --git a/package.json b/package.json
index 8fbd8105d6a..84e8b4b21a9 100644
--- a/package.json
+++ b/package.json
@@ -16,12 +16,8 @@
"test:watch": "tfm-test --watchAll",
"test:current": "tfm-test --watch",
"publish-coverage": "tfm-publish-coverage",
- "stories": "./script/npm-fix-foreman-stories.sh && tfm-stories",
- "build-storybook": "./script/npm-fix-foreman-stories.sh && tfm-build-stories",
- "deploy-storybook": "cross-env NODE_ENV=storybook NODE_OPTIONS=--max_old_space_size=8192 storybook-to-ghpages",
"postinstall": "./script/npm_install_plugins.js",
- "analyze": "./script/webpack-analyze",
- "create-react-component": "yo react-domain"
+ "analyze": "./script/webpack-analyze"
},
"dependencies": {
"@theforeman/vendor": "^12.0.1",
@@ -34,7 +30,6 @@
"@babel/core": "^7.7.0",
"@theforeman/builder": "^12.0.1",
"@theforeman/eslint-plugin-foreman": "^12.0.1",
- "@theforeman/stories": "^12.0.1",
"@theforeman/test": "^12.0.1",
"@theforeman/vendor-dev": "^12.0.1",
"@types/jest": "<27.0.0",
diff --git a/script/npm-fix-foreman-stories.sh b/script/npm-fix-foreman-stories.sh
deleted file mode 100755
index 4a6e064246f..00000000000
--- a/script/npm-fix-foreman-stories.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-# This script will allow you to run tfm-stories
-# while foreman can still use webpack-3 for the normal build.
-#
-# It is here temporarily until the migration to webpack-4 will be done.
-
-if [ "$NODE_ENV" = "production" ]; then
- exit 0
-fi
-
-if [ -d node_modules/@theforeman/stories/node_modules ]; then
- exit 0
-fi
-
-cd ./node_modules/@theforeman/stories
-npm install
\ No newline at end of file
diff --git a/script/npm_link_foreman_js.sh b/script/npm_link_foreman_js.sh
deleted file mode 100755
index df0bbb73c6a..00000000000
--- a/script/npm_link_foreman_js.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-# This script replace the npm installation of `foreman-js`
-# with your local version. Usefull when developing `foreman-js`
-# Read more about foreman-js: https://github.com/theforeman/foreman-js
-#
-# This script designed to run using `npm run foreman-js:link` in foreman root
-
-echo "This script is deprecated, please use the 'npm run link' in 'foreman-js'"
-echo "See the docs: https://foreman-storybook.surge.sh/?path=/docs/introduction-adding-dependencies--page"
-exit 1
diff --git a/webpack/assets/javascripts/react_app/components/BreadcrumbBar/BreadcrumbBar.stories.js b/webpack/assets/javascripts/react_app/components/BreadcrumbBar/BreadcrumbBar.stories.js
deleted file mode 100644
index 559e2e5ec99..00000000000
--- a/webpack/assets/javascripts/react_app/components/BreadcrumbBar/BreadcrumbBar.stories.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import { boolean, number } from '@storybook/addon-knobs';
-import { action } from '@storybook/addon-actions';
-
-import BreadcrumbBar from './BreadcrumbBar';
-import Story from '../../../../../stories/components/Story';
-
-export default {
- title: 'Components/BreadcrumbBar',
-};
-
-export const withOpenSwitcher = () => (
-
-
-
-);
-
-withOpenSwitcher.story = {
- name: 'With open switcher',
-};
diff --git a/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.fixtures.js b/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.fixtures.js
deleted file mode 100644
index cb1199d121a..00000000000
--- a/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.fixtures.js
+++ /dev/null
@@ -1,84 +0,0 @@
-const mockStoryData = {
- config: {
- donut: {
- width: 15,
- label: {
- show: false,
- },
- },
- data: {
- type: 'donut',
- columns: [
- ['Fedora 21', 3],
- ['Ubuntu 14.04', 4],
- ['Centos 7', 2],
- ['Debian 8', 1],
- ],
- names: {
- 'Fedora 21': 'Fedora 21',
- 'Ubuntu 14.04': 'Ubuntu 14.04',
- 'Centos 7': 'Centos 7',
- 'Debian 8': 'Debian 8',
- },
- },
- tooltip: {
- show: true,
- },
- legend: {
- show: false,
- },
- padding: {
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
- bindto: '#operatingsystemChart',
- },
- modalConfig: {
- donut: {
- width: 25,
- label: {
- show: false,
- },
- },
- data: {
- type: 'donut',
- columns: [
- ['Fedora 21', 3],
- ['Ubuntu 14.04', 4],
- ['Centos 7', 2],
- ['Debian 8', 1],
- ],
- names: {
- 'Fedora 21': 'Fedora 21',
- 'Ubuntu 14.04': 'Ubuntu 14.04',
- 'Centos 7': 'Centos 7',
- 'Debian 8': 'Debian 8',
- },
- },
- tooltip: {
- show: true,
- },
- legend: {
- show: false,
- },
- padding: {
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
- size: {
- height: 500,
- },
- },
- noDataMsg: 'No data available',
- tip: 'Expand the chart',
- status: 'RESOLVED',
- id: 'operatingsystem',
- title: 'OS Distribution',
- search: '/hosts?search=os_title=~VAL~',
-};
-
-export default mockStoryData;
diff --git a/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.stories.js b/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.stories.js
deleted file mode 100644
index 56951aee472..00000000000
--- a/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.stories.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from 'react';
-import ChartBox from './ChartBox';
-import mockStoryData from './ChartBox.fixtures';
-import Story from '../../../../../stories/components/Story';
-
-export default {
- title: 'Components/Charts/ChartBox',
- component: ChartBox,
-};
-
-export const Loading = () => (
-
-
-
-);
-
-export const WithoutData = () => (
-
-
-
-);
-
-export const WithError = () => (
-
-
-
-);
-
-export const WithDataAndModal = () => (
-
-
-
-);
-
-WithDataAndModal.story = {
- name: 'With Data + Modal',
-};
diff --git a/webpack/assets/javascripts/react_app/components/ConfigReports/DiffModal/DiffModal.stories.js b/webpack/assets/javascripts/react_app/components/ConfigReports/DiffModal/DiffModal.stories.js
deleted file mode 100644
index 2498463ecf7..00000000000
--- a/webpack/assets/javascripts/react_app/components/ConfigReports/DiffModal/DiffModal.stories.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from 'react';
-import { boolean, select } from '@storybook/addon-knobs';
-import { action } from '@storybook/addon-actions';
-
-import DiffModal from './DiffModal';
-import Story from '../../../../../../stories/components/Story';
-
-export default {
- title: 'Components/DiffModal',
-};
-
-export const diffModal = () => (
-
-
-
-);
-
-diffModal.story = {
- name: 'DiffModal',
-};
diff --git a/webpack/assets/javascripts/react_app/components/ConfirmModal/ConfirmModal.stories.js b/webpack/assets/javascripts/react_app/components/ConfirmModal/ConfirmModal.stories.js
deleted file mode 100644
index fe0d8642289..00000000000
--- a/webpack/assets/javascripts/react_app/components/ConfirmModal/ConfirmModal.stories.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from 'react';
-import { useDispatch } from 'react-redux';
-import { Button } from '@patternfly/react-core';
-import { action } from '@storybook/addon-actions';
-import { boolean, text, object } from '@storybook/addon-knobs';
-import storeDecorator from '../../../../../stories/storeDecorator';
-import Story from '../../../../../stories/components/Story';
-import ConfirmModal, { openConfirmModal } from '.';
-
-export default {
- title: 'Components/Confirm modal',
- decorators: [storeDecorator],
-};
-
-export const ConfirmBasicUsage = () =>
- React.createElement(() => {
- const dispatch = useDispatch();
- const isWarning = boolean('isWarning', false);
- const title = text('title', 'Confirm');
- const message = text('message', 'Are you sure?');
- const confirmButtonText = text('confirmButtonText', null);
- const handleConfirmClick = () => {
- dispatch(
- openConfirmModal({
- title,
- message,
- isWarning,
- confirmButtonText,
- onConfirm: action('Confirmed!'),
- onCancel: action('Canceled!'),
- })
- )
- };
-
- return (
-
-
- {/* The confirm modal is already declared on the app's root */}
-
-
- );
- });
-
- ConfirmBasicUsage.story = {
- name: 'Basic usage',
-};
diff --git a/webpack/assets/javascripts/react_app/components/DiffView/DiffView.stories.js b/webpack/assets/javascripts/react_app/components/DiffView/DiffView.stories.js
deleted file mode 100644
index ed732d42260..00000000000
--- a/webpack/assets/javascripts/react_app/components/DiffView/DiffView.stories.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import { text } from '@storybook/addon-knobs';
-
-import DiffContainer from './DiffContainer';
-import Story from '../../../../../stories/components/Story';
-
-export default {
- title: 'Components/DiffView',
-};
-
-export const diffView = () => (
-
-
-
-);
-
-diffView.story = {
- name: 'DiffView',
-};
diff --git a/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModal.js b/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModal.js
index ddf69541da2..fe1bec4f006 100644
--- a/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModal.js
+++ b/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModal.js
@@ -6,6 +6,30 @@ import ForemanModalHeader from './subcomponents/ForemanModalHeader';
import ForemanModalFooter from './subcomponents/ForemanModalFooter';
import { extractModalNodes } from './helpers';
+/**
+ * A modal component that provides a standardized layout and context for modals in Foreman.
+ * Should not be used in new components. use Patternfy 4 Modal instead.
+ * @param {string} id - The ID of the modal.
+ * @param {string} [title=''] - The title of the modal. will not be used if a custom header is provided.
+ * @param {boolean} [isOpen=false] - Whether the modal is open or not.
+ * @param {function} onClose - The function to call when the modal is closed.
+ * @param {boolean} [isSubmitting=false] - Whether the modal is currently submitting data or not.
+ * @param {Object} [submitProps={}] - Additional props to pass down to the submit button in the footer.
+ * @param {ReactNode} [children=null] - The child nodes of the modal.
+ * @returns {ReactNode} The rendered modal component.
+ */
+/*
+ Usage example for a custom header and footer:
+
+
+
This is a custom header! :)
+
+ body content
+
+ Custom footer
+
+
+*/
const ForemanModal = props => {
const {
id,
diff --git a/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModal.stories.js b/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModal.stories.js
deleted file mode 100644
index c1e5a25fe9c..00000000000
--- a/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModal.stories.js
+++ /dev/null
@@ -1,190 +0,0 @@
-import React from 'react';
-import { Button } from 'patternfly-react';
-import storeDecorator from '../../../../../stories/storeDecorator';
-import ForemanModal from '.';
-import { useForemanModal } from './ForemanModalHooks';
-import Story from '../../../../../stories/components/Story';
-
-export default {
- title: 'Components/ForemanModal/Props & Children',
- decorators: [storeDecorator],
-};
-
-export const withDefaultHeaderFooter = () =>
- // using createElement here so that hooks work in stories
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'default' });
- return (
-
-
-
-
- If you supply a title prop, it will be used as the modal title.
-
-
-
- );
- });
-
-withDefaultHeaderFooter.story = {
- name: 'With default header & footer',
-};
-
-export const withNoChildren = () =>
- // using createElement here so that hooks work in stories
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'noChildren' });
- return (
-
-
-
-
- );
- });
-
-withNoChildren.story = {
- name: 'With no children',
-};
-
-export const withCustomHeaderFooter = () =>
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'custom' });
- return (
-
-
-
-
-
This is a custom header! :)
-
- You can provide your own {``}
-
- Click the X in the upper right to close
-
-
-
- );
- });
-
-withCustomHeaderFooter.story = {
- name: 'With custom header & footer',
-};
-
-export const withUnorderedHeaderFooter = () =>
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'unordered' });
- return (
-
-
-
-
- Header and footer will be correctly ordered when rendering, even if
- they are out of order in the markup
-
- This is the footer
-
-
This is the header
-
-
-
- );
- });
-
-withUnorderedHeaderFooter.story = {
- name: 'With unordered header & footer',
-};
-
-export const withNoCloseButton = () =>
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'noClose' });
- return (
-
-
-
-
-
- Props passed to ForemanModal.Header will be passed down to
- Modal.Header
-
-
-
- );
- });
-
-withNoCloseButton.story = {
- name: 'With no close button',
-};
-
-export const withNoFooter = () =>
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'noFooter' });
- return (
-
-
-
-
- This is the modal body. There is no footer.
-
-
- );
- });
-
-withNoFooter.story = {
- name: 'With no footer',
-};
-
-export const withNoHeader = () =>
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'noHeader' });
- return (
-
-
-
- If neither a {``} nor a title prop are supplied,{' '}
-
- the modal will have no header.
-
-
-
- );
- });
-
-withNoHeader.story = {
- name: 'With no header',
-};
-
-export const withPropsPassedDownViaSpreadSyntax = () =>
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'propsPassed' });
- return (
-
-
-
-
- The inner {``} component will have any props you pass to
- {``}. (Look in the React dev tools for
- ‘myProp’)
-
-
-
- );
- });
-
-withPropsPassedDownViaSpreadSyntax.story = {
- name: 'With props passed down via spread syntax',
-};
diff --git a/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModalUsage.stories.js b/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModalUsage.stories.js
deleted file mode 100644
index 843ccb15c72..00000000000
--- a/webpack/assets/javascripts/react_app/components/ForemanModal/ForemanModalUsage.stories.js
+++ /dev/null
@@ -1,294 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Button } from 'patternfly-react';
-import { useDispatch, connect } from 'react-redux';
-import {
- setModalOpen as setModalOpenAction,
- setModalClosed as setModalClosedAction,
-} from './ForemanModalActions';
-import { selectIsModalOpen } from './ForemanModalSelectors';
-import storeDecorator from '../../../../../stories/storeDecorator';
-import ForemanModal from '.';
-import { useForemanModal } from './ForemanModalHooks';
-import Story from '../../../../../stories/components/Story';
-
-export default {
- title: 'Components/ForemanModal/ForemanModal Usage',
- decorators: [storeDecorator],
-};
-
-export const foremanModalBasics = () =>
- // using createElement here so that hooks work in stories
- React.createElement(() => {
- const { setModalOpen } = useForemanModal({ id: 'reduxModal' });
- return (
-
-
-
-
-
ForemanModal is controlled with Redux actions
-
- ForemanModals can be controlled from anywhere
-
- in the application, with a single source of truth for modal state.
-
-
How to use ForemanModal
-
ForemanModals require an ID prop.
-
It should be a descriptive unique string.
-
-
- {``}
-
-
-
-
- Once you've assigned an ID, modal state can be controlled in
- any of the following ways:
-
-
- In connected class components: By dispatching
- Redux actions with mapDispatchToProps
-
-
- In function components: With useForemanModal
- hook, which handles Redux for you
-
-
- In function components: With useSelector and
- useDispatch hooks from react-redux
-
-
-
-
See the stories below for examples.
-
-
-
- );
- });
-
-foremanModalBasics.story = {
- name: 'ForemanModal Basics',
-};
-
-export const withUseForemanModalHook = () =>
- // using createElement here so that hooks work in stories
- React.createElement(() => {
- const { modalOpen, setModalOpen } = useForemanModal({ id: 'hooks' });
- return (
-
-
-
-
- The useForemanModal hook returns 3 objects:
-
-
modalOpen: boolean
-
setModalOpen: function to open that specific modal
-
setModalClosed: function to close that specific modal
-
-
- These functions take care of the Redux state and actions for you,
-
- so you don't have to connect your parent component directly.
-
Click the STORY tab below to see the code.
-
-
-
- );
- });
-
-withUseForemanModalHook.story = {
- name: 'With useForemanModal Hook',
-};
-
-export const withReactReduxHooks = () =>
- // using createElement here so that hooks work in stories
- React.createElement(() => {
- const dispatch = useDispatch();
- return (
-
-
-
-
-
If you prefer, you can use the hooks provided by react-redux.
-
As before, make sure to assign your modal an ID prop:
-
-
- {``}
-
-
-
Modal state is controlled with Redux actions
-
- Control the modal state with the setModalOpen and setModalClosed
- actions. Make sure the ID passed to the action matches your ID prop.
-
- There are 4 date/time formats that should be used across the Foreman and
- plugins. Each of the formats is represented by one React component.
-
-
- Examples display {dateToShow.toString()}.
IsoDate
- Renders only date in iso format:
-
-
-
-
LongDateTime
- Renders full date with time. Relative time tooltip and seconds can be
- displayed optionally :
-
-
-
- There's an erb helper alternative for rendering the same format
- with relative time tooltip true as a default:
-
- date_time_absolute(time, :short, seconds = false,
- show_relative_time_tooltip = true)
-
-
ShortDateTime
- Renders shortened date with time. Relative time tooltip and seconds can
- be displayed optionally :
-
-
-
- There's an erb helper alternative for rendering the same format
- with relative time tooltip true as a default:
-
- date_time_absolute(time, :long, seconds = false,
- show_relative_time_tooltip = true)
-
-
RelativeDateTime
- Renders relative date with long date in a tooltip:
-
-
-
- There's an erb helper alternative for rendering a relative time:
- date_time_relative(time)
-
-
- ));
-
- return ;
-};
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/ForemanForm.stories.js b/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/ForemanForm.stories.js
deleted file mode 100644
index a51398aeb85..00000000000
--- a/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/ForemanForm.stories.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from 'react';
-import * as Yup from 'yup';
-import PropTypes from 'prop-types';
-
-import ForemanForm from '.';
-import TextField from '../TextField';
-import RadioButtonGroup from '../RadioButtonGroup';
-import Story from '../../../../../../../stories/components/Story';
-
-const DisplayFormikState = props => (
-
- );
-};
-OrderAppSandbox.propTypes = {
- options: PropTypes.object.isRequired,
-};
-
-export default {
- title: 'Components/Common/Orderable',
-};
-
-export const orderableStory = () => (
-
-
-
-
-
-);
-
-orderableStory.story = {
- name: 'Orderable',
-};
diff --git a/webpack/assets/javascripts/react_app/components/common/table/TableSelection.stories.mdx b/webpack/assets/javascripts/react_app/components/common/table/TableSelection.stories.mdx
deleted file mode 100644
index d301a41e117..00000000000
--- a/webpack/assets/javascripts/react_app/components/common/table/TableSelection.stories.mdx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { Meta } from '@theforeman/stories';
-import { Icon } from 'patternfly-react';
-
-
-
-# Table With Select boxes
-
-All needed functions and components are located at: `/react_app/components/common/table/index.js`
-To add select boxes to your foreman table please follow this steps:
-
-## Connect The reducer
-Create a unique reducer for your table by calling the `selectionReducer(tableID)` from the table folder to your component.
-**tableID** - a string that will represents the table in the store and will be used in the actions
-
-
-{' '}Tip: You can connect a few reducers to one component using Redux's
- combineReducers
-
-## Connect The Actions
-
-Create a `selectionController` using the `getSelectionController` helper
-
-```js
-getSelectionController({
- tableID,
- allRowsSelected,
- rows,
- selectedRows,
- dispatch,
-});
-```
-
-You will need to provide the function the following argumets:
-**tableID**: a string that will represents the table in the store
-**allRowsSelected**: a boolean that describes if all the rows available are selected.
-This boolean is provided by the selection reducer and should be in the components store
-**rows**: an array of row object that are available in the current page of the table. Each object should have an id.
-**selectedRows**: an array of the selected ids (if all rows are selected this can be empty) This array is provided by the selection reducer and should be in the components store.
-**dispatch**: dispatch function from the Redux store.
-Can be created using the useDispatch() hook.
-This is used for the selction actions.
-
-## Add To The Table TableScheme
-
-In the table schema array add the following item:
-
-```js
- column(
- '', // property
- 'Select all rows', // label
- [label => selectionHeaderCellFormatter(selectionController, label)], // headFormat
- [
- (value, additionalData) =>
- selectionCellFormatter(selectionController, additionalData),
- ] // cellFormat
- ),
-```
-
-The `property` is not used in the formatter so it should be empty.
-The `label` will be user for the tooltip information (title property) and aria-label.
-
-### Data provided by the reducer
-
-**allRowsSelected**: a boolean that describes if all the rows available are selected
-**selectedRows**: an array of the selected ids
-**showSelectAll**: a boolean that indicates should the select all option be available?
-This is true after a user is selection the whole page. Use this to show a select all button or message box.
-
-### Additional Actions
-
-If you want to use the selection action for uses outside the table you can use import selection actions from the table folder.
-Available actions:
-
-```js
-selectPage(tableID, rows);
-selectAllRows(tableID);
-unselectAllRows(tableID);
-selectRow(tableID, id);
-unselectRow(tableID, id, rows);
-```
diff --git a/webpack/assets/javascripts/react_app/components/common/table/schemaHelpers/selection.js b/webpack/assets/javascripts/react_app/components/common/table/schemaHelpers/selection.js
index 033e0f9a25a..36b1d54ffb8 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/schemaHelpers/selection.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/schemaHelpers/selection.js
@@ -5,6 +5,19 @@ import {
unselectRow,
} from '../actions/selectionActions';
+/**
+ * @property {string} tableID - A string that represents the table in the store.
+ * @property {boolean} allRowsSelected - A boolean that describes if all the rows available are selected.
+ * This boolean is provided by the selection reducer and should be in the component's store.
+ * @property {Object[]} rows - An array of row objects that are available in the current page of the table.
+ * Each object should have an id.
+ * @property {string[]} selectedRows - An array of the selected ids (if all rows are selected this can be empty).
+ * This array is provided by the selection reducer and should be in the component's store.
+ * @property {function} dispatch - Dispatch function from the Redux store.
+ * Can be created using the `useDispatch()` hook.
+ * This is used for the selection actions.
+ */
+
export const getSelectionController = ({
tableID,
allRowsSelected,
diff --git a/webpack/assets/javascripts/react_app/components/hosts/powerStatus/PowerStatus.stories.js b/webpack/assets/javascripts/react_app/components/hosts/powerStatus/PowerStatus.stories.js
deleted file mode 100644
index 6f6948bc57e..00000000000
--- a/webpack/assets/javascripts/react_app/components/hosts/powerStatus/PowerStatus.stories.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import React from 'react';
-import PowerStatus from './index';
-import {
- Story,
- Code,
- Text,
- StoryWithCustomState,
-} from '../../../../../../stories/components';
-import {
- pendingStore,
- resolvedStore,
- resolvedStoreWithOff,
- errorStore,
- serverProps,
-} from './PowerStatus.fixtures';
-
-export default {
- title: 'Components/Power Status',
-};
-
-export const loading = () => (
-
-
-
-);
-
-export const ON = () => (
-
-
-
-);
-
-export const OFF = () => (
-
-
-
-);
-
-export const errorStory = () => (
-
-
-
-);
-
-errorStory.story = {
- name: 'Error',
-};
-
-export const connectedMD = () => (
-
-
-
Using the connected component
-
-
- Currently the redux-connected component receives the following props:
-
- {'{ id, url }'}
-
- On the connected component mount we are making an API call with the
- given url,
-
-
- the state will update by it and the PowerStatus component will receive
- the following props from the selectors:
-
-```
-
-will render the following HTML:
-
-```html
-
-
-
-```
-
-(Note that the React component is rendered as a [web component](https://developer.mozilla.org/en-US/docs/Web/Web_Components).)
-
-### Changing the props from legacy JS
-
-We allow changing the root component props from the legacy JS.
-Be aware, that this will re-render the component.
-This feature should only be used for limited use cases:
-
-1. A legacy JS library/component needs to talk to a React component, AND
-1. The component is simple enough that it wouldn't otherwise make sense to store its data in the Redux store.
-
-We will use a method `reactProps` that our web component exposes and get the current props.
-Then we will change this props and use `reactProps=` setter that will trigger the rerender.
-
-
-```js
-var myCoolPowerElement = document.getElementById("my-cool-power-status").getElementsByTagName('foreman-react-component')[0];
-var newProps = myCoolPowerElement.reactProps;
-
-newProps.errorText = 'MyNewErrorText';
-myCoolPowerElement.reactProps = newProps;
-
-```
-
-
-*Note* that you can also directly set the data: `element.dataset.props = JSON.stringify({new: 'prop'});`
-or even the attribute: `element.setAttribute('data-props', JSON.stringify({new: 'prop'}));`
-
-Both of these need a JSON string as a new value to work.
-
-## Before you start writing a new component
-
-It's worth checking patternfly-react [Github repository](https://github.com/patternfly/patternfly-react) and [storybook](https://rawgit.com/patternfly/patternfly-react/gh-pages/index.html) to make sure such component doesn't exist yet. Also consider if your component is universal enough to be used in other projects. In such case it might make sense to add it to patternfly-react instead.
-
-Another place to look in is [move_to_foreman](https://github.com/Katello/katello/tree/master/webpack/move_to_foreman) folder in Katello. It already contains some components and code that is waiting to be moved to the core.
diff --git a/webpack/stories/docs/api-middleware.stories.mdx b/webpack/stories/docs/api-middleware.stories.mdx
deleted file mode 100644
index f12ee8e5c5f..00000000000
--- a/webpack/stories/docs/api-middleware.stories.mdx
+++ /dev/null
@@ -1,199 +0,0 @@
-import { Meta } from '@theforeman/stories';
-
-
-
-# API Middleware
-
-Instead of each component handling API calls in the same way we have the API Middleware that will handle it.
-
-`APIActions` can be imported from 'webpack/assets/javascripts/react_app/redux/API/index.js'
-or 'foremanReact/redux/API/index.js' for plugins
-
-`APIActions` contains the following methods: `get`, `post`, `put`, `delete`, `patch` which can be also imported independently, e.g:
-
-```js
-import { get, post, put, patch } from '../../redux/API';
-// can't import 'delete' as it's a reserved word in JS.
-```
-
-## How to use it in your action
-
-```js
-import { get } from '../../redux/API';
-
-const someAction = url =>
- get({
- key: MY_SPECIAL_KEY, // this will be used later to identify your API call, so keep it unique.
- url,
- params: {}, // some params you will want to pass to the API request.
- headers: {}, // some headers you will want to pass to the API request.
- });
-```
-
-or
-
-```js
-dispatch(
- get({
- key: MY_SPECIAL_KEY, // this will be used later to identify your API call, so keep it unique.
- url,
- params: {}, // some params you will want to pass to the API request.
- headers: {}, // some headers you will want to pass to the API request.
- })
-);
-```
-
-Then there will be called 2 actions: **MY_SPECIAL_KEY_REQUEST** and **MY_SPECIAL_KEY_SUCCESS/ MY_SPECIAL_KEY_FAILURE**:
-**MY_SPECIAL_KEY_REQUEST** will have the payload only
-**MY_SPECIAL_KEY_SUCCESS** will have the payload and the return data from the API call.
-**MY_SPECIAL_KEY_FAILURE** will have the payload and the return error from the API call.
-
-In the **payload** field you should send any headers and params for the GET request, and any other data you want for the action.
-
-The actions types can be changed with the optional **actionTypes** parameter:
-
-```js
-dispatch(
- get({
- key: MY_SPECIAL_KEY, // this will be used later to identify your API call, so keep it unique.
- url,
- params: {} // some params you will want to pass to the API request.
- headers: {}, // some headers you will want to pass to the API request.
- actionTypes: {
- REQUEST: 'CUSTOM_REQUEST',
- SUCCESS: 'CUSTOM_SUCCESS',
- FAILURE: 'CUSTOM_FAILURE',
- },
- })
-);
-```
-
-## Main API Reducer
-
-The API will manage the request lifecycle in the redux store > API > the provided key, e.g:
-
-```js
-{
- API: {
- MY_COMPONENT_KEY: { // this is the key you provided in the API action.
- status: 'RESOLVED',
- payload, // the payload you passed to the API action
- response, // the API response. it will store data on success or error on failure.
- }
- }
-}
-```
-
-it will manage the request statuses, errors and responses.
-
-### Access the API store
-
-We provided you the `selectAPIByKey` in '/APISelectors.js' which will return the key substate,
-there are also `selectAPIStatus`, `selectAPIPayload`, `selectAPIResponse`, `selectAPIError` and `selectAPIErrorMessage`.
-
-## Error handling
-
-In case you want to invoke some method or action immediately when an error occurs,
-we support the `handleError` param where you could pass an error handling function, e.g:
-
-```js
-dispatch(
- get({
- key: MY_SPECIAL_KEY,
- url,
- handleError: error => error.response.status === 401 ? logoutUser() : null
- ...
- })
-);
-```
-
-The error handling function will receive the error object from the API request.
-
-## Success handling
-
-In case you want to invoke some method or action immediately after the request succeed,
-we support the `handleSuccess` param where you could pass a success handling function, e.g:
-
-```js
-dispatch(
- get({
- key: MY_SPECIAL_KEY,
- url,
- handleSuccess: response => { dispatch(addToast({ type: 'success' })) },
- ...
- })
-);
-```
-
-The success handling function will receive the response object from the API request.
-
-In case you are also using the interval middleware with the same `KEY`,
-the `stopInterval` for your key will be passed as the second param in your `handleSuccess`/ `handleError` callbacks.
-
-## Toast handling
-
-Instead of managing and dispatching the toast action by yourself, you can use the API middleware shortcuts,
-by passing `toastSuccess` or `toastError` as keys in the API action, e.g:
-
-```js
-dispatch(
- get({
- key: MY_SPECIAL_KEY,
- url,
- toastError: error => `Oh no! Something went wrong while submitting the form, the server returned the following error: ${error}`
- toastSuccess: response => `Yay! task: ${response.taskID} was successfully created.`
- })
-);
-```
-
-## Update resources object
-
-After an API request SUCCESS action, it is often reconfirmed with a second GET request to view the updated data, and keep the Redux store up to date.
-However, if we receive a SUCCESS it is acceptable to assume that the data was actually updated as requested, and not completely necessary to make the additional request.
-To facilitate this, the API middleware can accept an `updateData` callback which returns the updated resource object corresponding to its key.
-Thus, Redux's copy of the backend data is changed based on this trust, and an additional API request to show the updated data is avoided.
-
-`updateData` callback accepts two arguments, prevState and currentResponse:
-
-#### prevState
-The previous response data which stored on redux store and corresponding with the given key.
-
-#### currentResponse
-The current API call response.
-
-
-Examples:
-
-```js
-// This action creates a post API call and only if it ends succesfully,
-// the response object will update with the returned data from 'updateData' callback
-dispatch(
- post({
- key: MY_SPECIAL_KEY,
- url,
- updateData: (prevResponseState, currentResponse) => ({
- results: prevStateData.filter(
- item => item.id !== currentResponse.id
- ),
- }),
- })
-);
-```
-
-```js
-post({
- key: MY_SPECIAL_KEY,
- url,
- params: { id: selectedID }
- updateData: prevResponseState => ({
- results: prevResponseState.filter(
- item => item.id !== selectedID
- ),
- }),
-})
-```
diff --git a/webpack/stories/docs/connected-react-router.stories.mdx b/webpack/stories/docs/connected-react-router.stories.mdx
deleted file mode 100644
index 63c251d6b71..00000000000
--- a/webpack/stories/docs/connected-react-router.stories.mdx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Meta } from '@theforeman/stories';
-
-
-
-# Connected React Router
-Foreman uses react-router for client routing in react pages, however foreman contains lots of erb content which includes react components.
-in order to control react router better and to have one source of truth,
-foreman uses `connected-react-router` which enable controling react router via redux.
-
-### Push url
-This pushed a new url (including querystring) to react router and creates a transition
-
-```js
-import { pushUrl } from '../foreman_navigation';
-import { foremanUrl } from '../foreman_tools';
-
-
-```
-
-### React Router Selectors
-You can use react router selectors for retriving data
-
-selectRouterLocation - the current location.
-electRouterPath - the current path
-selectRouterSearch - the current search
-selectRouterHash - the current hash
-selectRouter - the entire router object which includes every entry above
-
-```js
-import { useSelector } from 'react-redux';
-import { selectRouterLocation } from '../routes/RouterSelector';
-
-
-const location = useSelector(selectRouterLocation);
-```
diff --git a/webpack/stories/docs/creating-a-form.stories.mdx b/webpack/stories/docs/creating-a-form.stories.mdx
deleted file mode 100644
index 52e7bbccae5..00000000000
--- a/webpack/stories/docs/creating-a-form.stories.mdx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { Meta } from '@theforeman/stories';
-
-
-
-# Creating a Form
-
-Foreman uses [Formik](https://jaredpalmer.com/formik/) which takes care of repetitive code when creating forms.
-You can create a form with just Formik, however Foreman provides additional layer of obstraction over the Formik forms to make things more convenient and uniform. This is what a simple form might look like:
-
-```js
-// import things we need
-import React from 'react';
-import * as Yup from 'yup';
-// import ForemanForm and TextField specifying the relative path
-
-// define a validation schema using Yup
-const validationSchema = Yup.object().shape({
- name: Yup.string().required('is required'),
-});
-
-const FlavorForm = ({
- url,
- submitForm,
- controller,
- onCancel,
- initialValues,
-}) => (
-
- submitForm({
- url,
- values,
- message: 'New ice cream flavor successfully created!',
- })
- }
- initialValues={initialValues}
- validationSchema={validationSchema}
- onCancel={onCancel}
- >
-
- <\/ForemanForm>
-);
-```
-
-`ForemanForm` component requires the following props:
-
-- `onSubmit` - function called on form submission
-- `onCancel` - function called on form cancel
-- `initialValues` - object with initial values for the fields. For the example above, it could be: `{ name: 'Strawberry Super Sweetness' }`
-
-You can also pass a [Yup](https://github.com/jquense/yup) object as a `validationSchema`, which will take care of validations.
-
-### Toast Notifications
-
-Toast Notifications are enabled by default after a success or a failure.
-- `errorToast` - creates a toast notification based on `error.full_messages` object
-- `successToast` - creates a toast notification with the given `meesage` or the given `item` props
-
-You can override the default `successToast` or `errorToast` functions if needed:
-
-```js
-
- submitForm({
- ...otherProps,
- successToast: response => sprintf(__('a custom success message with %s'), response.data.item),
- errorToast: error => sprintf(__('a custom error message with %s'), error.response.data.error.item),
- })
- }
- >
-```
diff --git a/webpack/stories/docs/internationalization.stories.mdx b/webpack/stories/docs/internationalization.stories.mdx
deleted file mode 100644
index aedb5ef5686..00000000000
--- a/webpack/stories/docs/internationalization.stories.mdx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { Meta } from '@theforeman/stories';
-
-
-
-# Internationalization
-
-All strings need to be translated.
-
-### Foreman core
-
-`import I18n.js` which located in `/webpack/assets/javascripts/react_app/common`
-
-```js
-import {
- sprintf,
- translate as __,
- ngettext as n_,
-} from '../react_app/common/I18n';
-
-// - Sets the key to translate
-const str = __('a string');
-
-// - Plural version of translate
-const num_toasters = 2;
-const plural_str = n_(
- 'I have one toaster.',
- 'I have %1$d toasters.',
- num_toasters
-);
-```
-
-Use the `__` and `n_` aliasing, it is necessary for the string extration process
-
-### Plugins
-
-Same as foreman core, just use `foremanReact` alias:
-
-```js
-import {
- sprintf,
- translate as __,
- ngettext as n_,
-} from 'foremanReact/common/I18n';
-```
-
-## Interpolation
-
-Make sure that the strings inside the underscore function don't contain any interpolated values.
-Using interpolation would break the actual translation of the string in runtime. Use `sprintf` from `I18n` instead.
-
-**Example:**
-
-```js
-// Wrong:
-let msg = __(`%{taskCount} tasks complete`);
-```
-
-```js
-// Correct:
-import { sprintf } from '../react_app/common/I18n';
-let msg = sprintf(__('%(taskCount)s tasks complete'), { taskCount });
-```
-
-**Please note that the global underscore, sprintf and ngettext are deprecated**.
diff --git a/webpack/stories/docs/withReactRoutes.stories.mdx b/webpack/stories/docs/withReactRoutes.stories.mdx
deleted file mode 100644
index 9b5fb591322..00000000000
--- a/webpack/stories/docs/withReactRoutes.stories.mdx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Meta } from '@theforeman/stories';
-
-
-
-# withReactRoutes
-
-`withReactRoutes` wrapps your component with `Router` which gives you access to the react-router. This is useful for components in erb pages that need access to client routing - a good example is a button that redirects to a page in React:
-
-```js
-import { Link } from 'react-router-dom';
-
-import withReactRoutes from './react_app/common/withReactRoutes';
-
-const LinkButton = props => (
-
-
-
-);
-
-export default withReactRoutes(LinkButton);
-```
diff --git a/webpack/stories/store.js b/webpack/stories/store.js
deleted file mode 100644
index 0f2d4f399a2..00000000000
--- a/webpack/stories/store.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import createLogger from 'redux-logger';
-import configureMockStore from 'redux-mock-store';
-import thunk from 'redux-thunk';
-
-export default configureMockStore([thunk, createLogger()]);
diff --git a/webpack/stories/storeDecorator.js b/webpack/stories/storeDecorator.js
deleted file mode 100644
index ddcaf1fa9bc..00000000000
--- a/webpack/stories/storeDecorator.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-import store from '../assets/javascripts/react_app/redux';
-
-const storeDecorator = getStory => (
- {getStory()}
-);
-
-export default storeDecorator;