Skip to content

Commit

Permalink
Fixes #36439 - remove storybook and update ui docs
Browse files Browse the repository at this point in the history
  • Loading branch information
MariaAga authored and evgeni committed Jun 9, 2023
1 parent b6878a0 commit 5e7343e
Show file tree
Hide file tree
Showing 87 changed files with 357 additions and 3,860 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ vendor/ruby
package-lock.json
npm-debug.log
.vscode
.yo-rc.json
out*
.byebug_history
.gitignore
Expand Down
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ config/webpack.config.js
webpack/simple_named_modules.js
*.test.js
*fixture*
*.stories.js
*const*
34 changes: 0 additions & 34 deletions .github/workflows/storybook_deploy_main.yml

This file was deleted.

1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public/webpack
package-lock.json
npm-debug.log
.vscode
.yo-rc.json
.vendor/
.solargraph.yml
.nvmrc
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
[![Code Climate](https://codeclimate.com/github/theforeman/foreman/badges/gpa.svg)](https://codeclimate.com/github/theforeman/foreman)
[![Coverage Status](https://coveralls.io/repos/github/theforeman/foreman/badge.svg?branch=develop)](https://coveralls.io/github/theforeman/foreman?branch=develop)
[![Support IRC channel](https://kiwiirc.com/buttons/irc.libera.chat/theforeman.png)](https://kiwiirc.com/client/irc.libera.chat/?#theforeman)
[![Storybook](https://raw.githubusercontent.com/storybooks/brand/master/badge/badge-storybook.svg)](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.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import { Meta } from '@theforeman/stories';
[[adding-js-dependencies]]
= NPM dependencies
:toc: right
:toclevels: 5

<Meta
title="Introduction/Adding dependencies"
parameters={{
storyWeight: 30,
}}
/>

# 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:

Expand Down
116 changes: 116 additions & 0 deletions developer_docs/adding-new-components.asciidoc
Original file line number Diff line number Diff line change
@@ -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/<COMPONENT_NAME>/
├─ <COMPONENT_NAME>.js ┈ react component
├─ <COMPONENT_NAME>.scss ┈ styles if needed
├─ <COMPONENT_NAME>.fixtures.js ┈ constants for testing, initial state, etc.
├─ <COMPONENT_NAME>.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/<COMPONENT_NAME>/
├─ <COMPONENT_NAME>.js ┈ pure react component
├─ <COMPONENT_NAME>.scss ┈ styles if needed
├─ <COMPONENT_NAME>Actions.js ┈ redux actions
├─ <COMPONENT_NAME>Reducer.js ┈ redux reducer
├─ <COMPONENT_NAME>Selectors.js ┈ reselect selectors
├─ <COMPONENT_NAME>Constants.js ┈ constants such as action types
├─ <COMPONENT_NAME>.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 \-- <path to test file>`. 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
<div id="my-cool-power-status">
<%= react_component('PowerStatus', id: host.id, url: power_host_path(host.id), errorText: 'N/A') %>
</div>
```

will render the following HTML:

```html
<div id="my-cool-power-status">
<foreman-react-component
name="PowerStatus"
data-props="<%= {
id: host.id,
url: power_host_path(host.id),
errorText: 'N/A',
}.to_json %>"
></foreman-react-component>
</div>
```

(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.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Meta } from '@theforeman/stories';
[[api-middleware-intro]]

<Meta
title="Introduction/API Middleware Usage"
parameters={{
storyWeight: 100,
}}
/>
= 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.

Expand All @@ -19,39 +20,37 @@ 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',
};
const {
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 (
<ul>
{audits.map(item => (
<li key={item.id}>
{item.title} {item.action}
</li>
<button onClick={() => setAPIOptions({ ...options, params: { search: 'os=fedora' } })}>
Fedora
</button>
<button onClick={() => setAPIOptions({ ...options, params: { search: 'os=debian' } })}>
Debian
</button>
{results.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
};
```

# 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*/
Expand Down Expand Up @@ -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*/

Expand All @@ -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*/
Expand All @@ -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:

Expand Down

0 comments on commit 5e7343e

Please sign in to comment.