Skip to content

Commit

Permalink
Merge pull request #38 from fhlavac/guide
Browse files Browse the repository at this point in the history
Init a contribution guide
  • Loading branch information
fhlavac authored Sep 13, 2023
2 parents 75481c9 + f62c881 commit 521a280
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 61 deletions.
128 changes: 128 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,134 @@

This repo contains a set of opinionated react component groups used to standardize functionality and look and feel across products. The components are based of PatternFly with some additional functionality.

## Contribution guide

### Before adding a new component:
- make sure your use case is new/complex enough to be added to this extension
- the component should bring a value value above and beyond existing PatternFly components

### To add a new component:
1. create a folder in `src/` matching its name (for example `src/MyComponent`)
2. to the new folder add a new `.tsx` file named after the component (for example `src/MyComponent/MyComponent.tsx`)
3. to the same folder include also a `index.ts` which will export the component as a default and then all necessary interfaces
4. if this file structure is not met, your component won't be exposed correctly

#### Example component:
```
import * as React from 'react';
import { Text } from '@patternfly/react-core';
import { createUseStyles } from 'react-jss';
// do not forget to export your component's interface
// always place component's interface above the component itself in the code
export interface MyComponentProps {
text: String;
}
const useStyles = createUseStyles({
myText: {
fontFamily: 'monospace',
fontSize: 'var(--pf-v5-global--icon--FontSize--md)',
},
})
// do not use named export of your component, just a default one
const MyComponent: React.FunctionComponent<MyComponentProps> = () => {
const classes = useStyles();
return (
<Text className={classes.myText}>
This is my new reusable component
</Text>
);
};
export default MyComponent;
```

#### Index file example:
```
export { default } from './MyComponent';
export * from './MyComponent';
```

#### Component directory structure example:
```
src
|- MyComponent
|- index.ts
|- MyComponent.tsx
```

### Component's API rules:
- prop names comply with PatternFly components naming standards (`variant`, `onClick`, `position`, etc.)
- the API is maximally simplified and all props are provided with a description
- it is build on the top of existing PatternFly types without prop omitting
- it is well documented using the PatternFly documentation (`/packages/module/patternfly-docs/content/extensions/component-groups/examples/MyComponent/MyComponent.md`) with examples of all possible use cases (`packages/module/patternfly-docs/content/extensions/component-groups/examples/MyComponent/MyComponent[...]Example.tsx`)
- do not unnecessarily use external libraries in your component - rather, delegate the necessary logic to the component's user using the component's API

#### Component API definition example:
```
// when possible, extend available PatternFly types
export interface MyComponentProps extends ButtonProps {
customLabel: Boolean
};
export const MyComponent: React.FunctionComponent<MyComponentProps> = ({ customLabel, ...props }) => ( ... );
```


#### Markdown file example:
```
---
section: extensions
subsection: Component groups
id: MyComponent
propComponents: ['MyComponent']
---
import MyComponent from "@patternfly/react-component-groups/dist/dynamic/MyComponent";
## Component usage
MyComponent has been created to demo contributing to this repository.
### MyComponent component example label
```js file="./MyComponentExample.tsx"```

```
#### Component usage file example: (`MyComponentExample.tsx`)
```
import React from 'react';

const MyComponentExample: React.FunctionComponent = () => (
<MyComponent customLabel="My label">
);

export default BatteryLowExample;
```
### Sub-components:
When adding a component for which it is advantageous to divide it into several sub-components make sure:
- component and all its sub-components are located in separate files and directories straight under the `src/` folder
- sub-components are exported and documented separately from their parent
- parent component should provide a way to pass props to all its sub-components
The aim is to enable the user of our "complex" component to use either complete or take advantage of its sub-components and manage their composition independently.
### Testing:
When adding/making changes to a component, always make sure your code is tested:
- use React Testing Library for testing
- add tests to a `[ComponentName].test.tsx` file to your component's directory
- make sure all the core logic is covered
### Styling:
- for styling always use JSS
- new classNames should match PatternFly conventions (`pf-cg-[component]-[more-details]`)
- new CSS variables should be named like `--pf-cg-[my-variable]-[more-details]`
## Building for production
- run npm install
Expand Down
74 changes: 37 additions & 37 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 0 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,6 @@
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.21.4",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-config-prettier": "8.6.0",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"prettier": "2.8.3",
"jest":"^29.2.2",
"babel-jest":"^29.2.2",
"@babel/core": "^7.19.6",
"@babel/preset-env": "^7.19.4",
"@babel/preset-react":"^7.18.6",
"@babel/preset-flow": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@testing-library/react":"^13.4.0",
"@testing-library/user-event": "14.4.3",
"@testing-library/jest-dom":"^5.14.1",
"@testing-library/dom": "^8.0.7",
"jest-environment-jsdom": "^29.5.0",
"jest-canvas-mock": "^2.4.0",
"ts-jest":"29.0.3",
"serve": "^14.1.2",
"rimraf": "^2.6.2",
"whatwg-fetch": "^3.6.2",
"babel-polyfill": "6.26.0",
"identity-obj-proxy": "^3.0.0",
"fs-extra": "^9.1.0",
"glob": "^7.2.3",
"identity-obj-proxy": "^3.0.0",
Expand Down
44 changes: 44 additions & 0 deletions packages/module/generate-index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const fse = require('fs-extra');
const glob = require('glob');
const path = require('path');

const root = process.cwd();

const ENV_AGNOSTIC_ROOT = `${root}/src`

const sourceFiles = glob.sync(path.resolve(__dirname, './src/*/index.ts'))

async function generateIndex(files) {
// ensure the dynamic root exists
fse.ensureDirSync(path.resolve(ENV_AGNOSTIC_ROOT));

const destFile = `${ENV_AGNOSTIC_ROOT}/index.ts`;

const stream = fse.createWriteStream(destFile);
stream.once('open', () => {
stream.write('// this file is autogenerated by generate-index.js, modifying it manually will have no effect\n');

files.forEach(file => {
const name = file.replace('/index.ts', '').split('/').pop();
stream.write(`\nexport { default as ${name} } from './${name}';\n`);
stream.write(`export * from './${name}';\n`);
});
stream.end();
});

return Promise.resolve();
}

async function run(files) {
try {
await generateIndex(files);

} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
}
}

run(sourceFiles);

3 changes: 2 additions & 1 deletion packages/module/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"scripts": {
"build": "npm run build:js && npm run build:esm && npm run build:fed:packages",
"build": "npm run build:index && npm run build:js && npm run build:esm && npm run build:fed:packages",
"build:watch": "npm run build:js && npm run build:esm -- --watch && npm run build:fed:packages -- --watch",
"build:esm": "tsc --build --verbose ./tsconfig.json",
"build:fed:packages": "node generate-fed-package-json.js",
"build:js": "tsc -p tsconfig.cjs.json",
"build:index": "node generate-index.js",
"clean": "rimraf dist",
"docs:develop": "pf-docs-framework start",
"docs:build": "pf-docs-framework build all --output public",
Expand Down
18 changes: 18 additions & 0 deletions packages/module/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
// this file is autogenerated by generate-index.js, modifying it manually will have no effect

export { default as Battery } from './Battery';
export * from './Battery';

export { default as DetailsPage } from './DetailsPage';
export * from './DetailsPage';

export { default as DetailsPageHeader } from './DetailsPageHeader';
export * from './DetailsPageHeader';

export { default as ErrorBoundary } from './ErrorBoundary';
export * from './ErrorBoundary';

export { default as ErrorStack } from './ErrorStack';
export * from './ErrorStack';

export { default as ErrorState } from './ErrorState';
export * from './ErrorState';

export { default as HorizontalNav } from './HorizontalNav';
export * from './HorizontalNav';

export { default as NotAuthorized } from './NotAuthorized';
export * from './NotAuthorized';

0 comments on commit 521a280

Please sign in to comment.