Skip to content

Commit

Permalink
feat(storybook): Replace TestComponent with StatusIcon, add stories a…
Browse files Browse the repository at this point in the history
…nd tests (#13)

* build(package.json): install @storybook/addon-docs and peer dependencies

* feat(statusicon): add StatusIcon component, tests and stories

* feat(storybook): switch to MDX docs mode for stories

* fix(storybook): import PatternFly CSS in Storybook

* feat(storybook): add GitHub source link at the bottom of the story

* fix(index.ts): fix bad export

* chore(readme): add a note about MDX
  • Loading branch information
mturley authored Aug 26, 2020
1 parent 49ca111 commit d34a515
Show file tree
Hide file tree
Showing 16 changed files with 1,210 additions and 116 deletions.
14 changes: 14 additions & 0 deletions .storybook/helpers/GithubLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as React from 'react';
import { GithubIcon } from '@patternfly/react-icons';

interface IGithubLinkProps {
path: string;
}

const GithubLink: React.FunctionComponent<IGithubLinkProps> = ({ path }) => (
<a href={`https://github.com/konveyor/lib-ui/blob/master/${path}`}>
<GithubIcon /> View Source on GitHub
</a>
);

export default GithubLink;
7 changes: 5 additions & 2 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const path = require('path');

module.exports = {
stories: ['../src/**/*.stories.tsx'],
stories: ['../src/**/*.stories.@(tsx|mdx)'],
// Add any Storybook addons you want here: https://storybook.js.org/addons/
addons: [],
addons: ['@storybook/addon-docs'],
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.scss$/,
Expand All @@ -18,6 +18,9 @@ module.exports = {
presets: [['react-app', { flow: false, typescript: true }]],
},
});

// TODO do we need to import more CSS here for PF than the base.css from preview.js?

config.resolve.extensions.push('.ts', '.tsx');

return config;
Expand Down
1 change: 1 addition & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '../node_modules/@patternfly/react-core/dist/styles/base.css';
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ We should avoid leaving apps configured this way; installing from npm will be mo

This package has React and PatternFly packages as peer dependencies, which are not included in the library bundle. That way, your app can also depend on them directly without bundling them twice.

When you install @konveyor/lib-ui, you should get a warning from your package manager telling you which versions to install. Make sure you have the right versions as dependencies in your app.
When you install @konveyor/lib-ui, you should get a warning from your package manager telling you which versions to install. [Make sure you have compatible versions](https://github.com/konveyor/lib-ui/blob/master/package.json#L30) as dependencies in your app.

### Use it!

Expand Down Expand Up @@ -123,7 +123,7 @@ Components live in `src/MyComponent/` directories, which should each contain:

- `MyComponent.tsx` - component source and type interfaces (types can be their own file if they are verbose enough)
- `MyComponent.scss` - any custom styles not covered by PatternFly, we should avoid these when possible
- `MyComponent.stories.tsx` - define your [Storybook stories](https://storybook.js.org/docs/react/get-started/whats-a-story) (examples and docs) for your component
- `MyComponent.stories.mdx` - define your [Storybook stories](https://storybook.js.org/docs/react/get-started/whats-a-story) (examples and docs) for your component. We are using the [MDX story format](https://storybook.js.org/docs/react/writing-docs/mdx).
- `MyComponent.test.tsx` - unit tests using [jest](https://jestjs.io/) and [react-testing-library](https://testing-library.com/docs/react-testing-library/intro)
- `index.ts` - define your exports for the component directory

Expand All @@ -134,7 +134,7 @@ When you add a new component, be sure to also export it at the top level (`src/i
## TODO:

- Add docs extension for Storybook
- Test importing the TestComponent into another project via GitHub dependency href? Can we do that?
- Test importing into another project via GitHub dependency href? Can we do that?
- Add reusable components from mig-ui and virt-ui
- Start actually using the thing
- Unit tests?
21 changes: 13 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,22 @@
"ci": "yarn type-check && yarn lint && yarn test",
"clean": "rimraf dist && rimraf storybook-static",
"build": "rimraf dist && rollup -c",
"storybook": "start-storybook -p 6006",
"storybook:export": "rimraf storybook-static && build-storybook"
"storybook": "start-storybook --docs -p 6006",
"storybook:export": "rimraf storybook-static && build-storybook --docs"
},
"peerDependencies": {
"@patternfly/react-core": "^4.40.4",
"@patternfly/react-tokens": "^4.9.4",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"@babel/core": "^7.11.1",
"@patternfly/react-core": "^4.40.4",
"@patternfly/react-tokens": "^4.9.4",
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-node-resolve": "^8.4.0",
"@storybook/addon-docs": "^6.0.18",
"@storybook/addons": "^6.0.16",
"@storybook/react": "^6.0.16",
"@storybook/theming": "^6.0.16",
Expand All @@ -56,6 +64,7 @@
"prettier": "^2.0.5",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-is": "^16.8.0",
"rimraf": "^3.0.2",
"rollup": "^2.23.0",
"rollup-plugin-peer-deps-external": "^2.2.3",
Expand All @@ -64,12 +73,8 @@
"sass-loader": "^9.0.3",
"semantic-release": "^17.1.1",
"ts-jest": "^26.2.0",
"typescript": "^3.9.7"
},
"peerDependencies": {
"@patternfly/react-core": "^4.40.4",
"react": "^16.13.1",
"react-dom": "^16.13.1"
"typescript": "^3.9.7",
"webpack": "^4.44.1"
},
"config": {
"commitizen": {
Expand Down
16 changes: 0 additions & 16 deletions src/TestComponent/TestComponent.scss

This file was deleted.

10 changes: 0 additions & 10 deletions src/TestComponent/TestComponent.stories.tsx

This file was deleted.

33 changes: 0 additions & 33 deletions src/TestComponent/TestComponent.test.tsx

This file was deleted.

14 changes: 0 additions & 14 deletions src/TestComponent/TestComponent.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/TestComponent/index.ts

This file was deleted.

52 changes: 52 additions & 0 deletions src/components/StatusIcon/StatusIcon.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks';
import { StatusIcon, StatusType } from './StatusIcon';
import GithubLink from '../../../.storybook/helpers/GithubLink';

<Meta title="Components/StatusIcon" component={StatusIcon} />

# StatusIcon

A small wrapper for PatternFly's CheckCircleIcon,
WarningTriangleIcon and ExclamationCircleIcon that automatically sets the right color.

## Examples

### Basic

<Canvas>
<Story name="Basic">
<StatusIcon status={StatusType.Ok} />
<br />
<StatusIcon status={StatusType.Warning} />
<br />
<StatusIcon status={StatusType.Error} />
</Story>
</Canvas>

### Disabled

<Canvas>
<Story name="Disabled">
<StatusIcon status={StatusType.Ok} isDisabled />
<br />
<StatusIcon status={StatusType.Warning} isDisabled />
<br />
<StatusIcon status={StatusType.Error} isDisabled />
</Story>
</Canvas>

### With label

<Canvas>
<Story name="With label">
<StatusIcon status={StatusType.Ok} label="Ready" />
<StatusIcon status={StatusType.Warning} label="Warning" />
<StatusIcon status={StatusType.Error} label="Error" />
</Story>
</Canvas>

## Props

<ArgsTable of={StatusIcon} />

<GithubLink path="src/components/StatusIcon/StatusIcon.tsx" />
76 changes: 76 additions & 0 deletions src/components/StatusIcon/StatusIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import {
global_disabled_color_200 as disabledColor,
global_success_color_100 as successColor,
global_warning_color_100 as warningColor,
global_danger_color_100 as dangerColor,
} from '@patternfly/react-tokens';

import { StatusIcon, StatusType, IStatusIconProps } from './StatusIcon';

const checkColor = (props: IStatusIconProps, color: string) => {
const { container } = render(<StatusIcon {...props} />);
const icon = container.querySelector('svg');
expect(icon).toHaveAttribute('fill', color);
};

const checkClass = (props: IStatusIconProps, className: string) => {
const { container } = render(<StatusIcon {...props} />);
const icon = container.querySelector('svg');
expect(icon).toHaveClass(className);
};

const checkText = (props: IStatusIconProps, text: string) => {
const { container } = render(<StatusIcon {...props} />);
const icon = container.querySelector('.pf-l-flex');
expect(icon).toContainHTML(text);
};

describe('StatusIcon', () => {
describe('Ok status', () => {
it('should have label if present', () => {
checkText({ status: StatusType.Ok, label: 'Ready' }, 'Ready');
});
it('should have correct color', () => {
checkColor({ status: StatusType.Ok }, successColor.value);
});
it('should have disabled color if disabled', () => {
checkColor({ status: StatusType.Ok, isDisabled: true }, disabledColor.value);
});
it('should pass down a given className', () => {
checkClass({ status: StatusType.Ok, className: 'foo' }, 'foo');
});
});

describe('Warning status', () => {
it('should have label if present', () => {
checkText({ status: StatusType.Warning, label: 'Warning' }, 'Warning');
});
it('should have correct color', () => {
checkColor({ status: StatusType.Warning }, warningColor.value);
});
it('should have disabled color if disabled', () => {
checkColor({ status: StatusType.Warning, isDisabled: true }, disabledColor.value);
});
it('should pass down a given className', () => {
checkClass({ status: StatusType.Warning, className: 'foo' }, 'foo');
});
});

describe('Error status', () => {
it('should have label if present', () => {
checkText({ status: StatusType.Error, label: 'Error' }, 'Error');
});
it('should have correct color', () => {
checkColor({ status: StatusType.Error }, dangerColor.value);
});
it('should have disabled color if disabled', () => {
checkColor({ status: StatusType.Error, isDisabled: true }, disabledColor.value);
});
it('should pass down a given className', () => {
checkClass({ status: StatusType.Error, className: 'foo' }, 'foo');
});
});
});
68 changes: 68 additions & 0 deletions src/components/StatusIcon/StatusIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as React from 'react';
import { Flex, FlexItem } from '@patternfly/react-core';
import {
CheckCircleIcon,
WarningTriangleIcon,
ExclamationCircleIcon,
} from '@patternfly/react-icons';
import {
global_disabled_color_200 as disabledColor,
global_success_color_100 as successColor,
global_warning_color_100 as warningColor,
global_danger_color_100 as dangerColor,
} from '@patternfly/react-tokens';

export enum StatusType {
Ok = 'Ok',
Warning = 'Warning',
Error = 'Error',
}

export interface IStatusIconProps {
status: StatusType;
label?: string;
isDisabled?: boolean;
className?: string;
}

export const StatusIcon: React.FunctionComponent<IStatusIconProps> = ({
status,
label,
isDisabled = false,
className = '',
}: IStatusIconProps) => {
let icon: React.ReactElement | null = null;
if (status === StatusType.Ok) {
icon = (
<CheckCircleIcon
className={className}
color={isDisabled ? disabledColor.value : successColor.value}
/>
);
}
if (status === StatusType.Warning) {
icon = (
<WarningTriangleIcon
className={className}
color={isDisabled ? disabledColor.value : warningColor.value}
/>
);
}
if (status === StatusType.Error) {
icon = (
<ExclamationCircleIcon
className={className}
color={isDisabled ? disabledColor.value : dangerColor.value}
/>
);
}
if (label) {
return (
<Flex spaceItems={{ default: 'spaceItemsSm' }}>
<FlexItem>{icon}</FlexItem>
<FlexItem>{label}</FlexItem>
</Flex>
);
}
return icon;
};
1 change: 1 addition & 0 deletions src/components/StatusIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './StatusIcon';
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './TestComponent';
export * from './components/StatusIcon';
Loading

0 comments on commit d34a515

Please sign in to comment.