feat: OHIF-v3 UI Component Library (#2653)

* wip on storybook as a replacement for using UI components in docusaurus, since it causes too many problems

* fix: storybook webpack config

* feat: add more stories

* feat: add more stories

* feat: add more stories

* feat: fix logo and babel runtime error

* feat: move to mdx docs for ui

* feat: enhanced mdx for header and styles

* feat: Add button usecases

* feat: Add buttonGroup docs

* feat: Add docs and use case to cine and contex menu

* feat: fix decorators

* feat: add header and dropdown docs

* feat: add Icon and input docs

* feat: add more docs

* feat: add more stories

* fix: feedback section

* feat: Add doc page

* feat: Add typography and color stories

* yarn lock

* add analytics to v3-ui

* new banner image

* new viewport component

* update review comments
This commit is contained in:
Alireza authored and GitHub committed 2022-02-10 14:08:34 -05:00
1 parent eb77cb76b6
commit 5c8ee7f906
79 files changed
+9004 -2915

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

@@ -0,0 +1,6 @@
import React from 'react';
// Create a square box with a given color
export const Color = ({ color }) => {
return <div className="w-8 h-8" style={{ backgroundColor: color }} />;
};
@@ -0,0 +1,117 @@
import { Meta } from '@storybook/addon-docs';
import { Color } from './color/color';
<Meta title="Foundations/Color" />
## Overview
Below you can see the color palette of the application and the className to use
for each color.
<br />
### Primary Colors
<div className="grid grid-cols-4 gap-4 h-auto w-full bg-gray-200 p-8">
<div className="flex space-x-2 items-center">
<div className="bg-primary-dark w-16 h-16" />
<span>primary-dark</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-primary-light w-16 h-16" />
<span>primary-light</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-primary-main w-16 h-16" />
<span>primary-main</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-primary-active w-16 h-16" />
<span>primary-active</span>
</div>
</div>
<br />
#### Usage
Therefore you can use the className to style the color of a component (using
`bg-${color-class}`) or text (using `text-${color-class}`).
```jsx
<div className="bg-primary-main text-primary-light">OHIF UI Components</div>
```
to render
<div className="bg-primary-main text-primary-light">OHIF UI Components</div>
<br />
<br />
### Secondary Colors
<div className="grid grid-cols-4 gap-4 h-auto w-full bg-gray-200 p-8">
<div className="flex space-x-2 items-center">
<div className="bg-secondary-dark w-16 h-16" />
<span>secondary-dark</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-secondary-light w-16 h-16" />
<span>secondary-light</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-secondary-main w-16 h-16" />
<span>secondary-main</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-secondary-active w-16 h-16" />
<span>secondary-active</span>
</div>
</div>
<br />
### Common Colors
<div className="grid grid-cols-4 gap-4 h-auto w-full bg-gray-200 p-8">
<div className="flex space-x-2 items-center">
<div className="bg-common-bright w-16 h-16" />
<span>common-bright</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-common-light w-16 h-16" />
<span>common-light</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-common-main w-16 h-16" />
<span>common-main</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-common-dark w-16 h-16" />
<span>common-dark</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-common-active w-16 h-16" />
<span>common-active</span>
</div>
</div>
<br />
### Custom Colors
<div className="grid grid-cols-4 gap-4 h-auto w-full bg-gray-200 p-8">
<div className="flex space-x-2 items-center">
<div className="bg-customgreen-100 w-16 h-16" />
<span>customgreen-100</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-customblue-100 w-16 h-16" />
<span>customblue-100</span>
</div>
<div className="flex space-x-2 items-center">
<div className="bg-customblue-200 w-16 h-16" />
<span>customblue-200</span>
</div>
</div>
@@ -0,0 +1,3 @@
export const AnchorListItem = ({ children }) => {
return <li className="text-gray-800">{children}</li>;
};
@@ -0,0 +1,19 @@
import React from 'react';
export const Footer = ({ componentRelativePath }) => {
return (
<div>
<p className="my-2 text-gray-800 font-inter">
We always welcome feedback and suggestions. If you are interested to add
additional usecases, please send us a pull request. Edit this page on{' '}
<a
className="text-blue-400"
href={`https://github.com/OHIF/Viewers/edit/v3-stable/platform/ui/src/components/${componentRelativePath}`}
>
Github
</a>
.
</p>
</div>
);
};
@@ -0,0 +1,46 @@
import React from 'react';
function GitHubLogo() {
return (
<svg
className="inline"
width="18"
height="18"
viewBox="0 0 1024 1024"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
transform="scale(64)"
fill="currentColor"
/>
</svg>
);
}
export const Heading = ({ title, componentRelativePath }) => {
return (
<div className="flex items-center w-full">
<div>
<h1 className="text-4xl font-normal text-blue-600 font-inter">
{title}
</h1>
</div>
{componentRelativePath && (
<div className="ml-4">
<a
href={`https://github.com/OHIF/Viewers/blob/v3-stable/platform/ui/src/components/${componentRelativePath}`}
className="text-sm font-normal text-blue-600"
target="_blank"
rel="noopener noreferrer"
>
<GitHubLogo />
</a>
</div>
)}
</div>
);
};
@@ -0,0 +1,17 @@
import React from 'react';
// centers the image in the container in the page
export const WithImage = ({ children, image, alt, className }) => (
<div className={className}>
<img
src={image}
alt={alt}
style={{
display: 'block',
marginLeft: 'auto',
marginRight: 'auto',
}}
/>
{children}
</div>
);
@@ -0,0 +1,17 @@
// https://github.com/mondaycom/monday-ui-react-core/tree/master/src/storybook
//
import { Heading } from './heading/heading';
import { AnchorListItem } from './anchor-list-item/anchor-list-item';
import { LinkComponent } from './link-component/link-component';
import { SectionName } from './section-name/section-name';
import { Footer } from './footer/footer';
import { WithImage } from './image/image';
export {
Heading,
AnchorListItem,
LinkComponent,
SectionName,
Footer,
WithImage,
};
@@ -0,0 +1,32 @@
import React from 'react';
export const LinkComponent = ({
children,
href,
target: originalTarget,
rel,
}) => {
const onClick = (event) => {
if (originalTarget) {
return;
}
const target = document.querySelector(href);
if (target) {
event.preventDefault();
event.stopPropagation();
target.scrollIntoView();
}
};
return (
<a
className="text-blue-600"
onClick={onClick}
target={originalTarget}
rel={rel}
href={href}
>
{children}
</a>
);
};
@@ -0,0 +1,23 @@
import PropTypes from 'prop-types';
import { useMemo } from 'react';
export const SectionName = ({ className, children, ...props }) => {
const id = useMemo(
() =>
children
.toLowerCase()
.split(' ')
.join('-'),
[children]
);
// eslint-disable-next-line jsx-a11y/heading-has-content
return (
<h2 id={id} {...props} className="mt-4 text-3xl text-blue-600">
{children}
</h2>
);
};
SectionName.propTypes = {
children: PropTypes.string.isRequired,
};
@@ -0,0 +1,110 @@
import { Meta } from '@storybook/addon-docs';
import { WithImage } from './components';
import { WelcomeHeader } from './header/header';
import StoryBook from './assets/storybook.png';
import CodeBlock from './assets/codeblock-error.png';
<Meta title="Contribute" />
## How to Contribute to the Docs
We are always looking for new contributors. If you are interested in
contributing to the docs, please follow below instructions to run the storyBook
locally and submit your new stories.
<br />
### Install OHIF Packages
Install OHIF Packages using `yarn install` command.
<br />
### Change Directory to OHIF UI
Change directory to OHIF UI using `cd platform/ui` command.
<br />
### Run Storybook
You can now run the storybook using `yarn storybook` command. This will open the
storybook in your browser. You should be able to interact with Storybook and the
already existing stories.
<WithImage image={StoryBook} alt={'storybook'} />
<br />
### Writing a Story
Stories for each UI component are stored in the `__stories__` folder. Stories
are written in the `mdx` file format. You can read more about `mdx` file format
[here](https://storybook.js.org/docs/react/api/mdx). In summary, the `mdx` file
format is a way to write stories in a plain text format, but with the ability to
use React components.
For Components that don't have a `mdx` file, you need to create a `mdx` file,
Storybook automatically fetch the `mdx` file and render the story. You can use
one of the already existing `mdx` files for the component (e.g.
`button.stories.mdx`). If there is already a `mdx` file for the component, you
can add you usage in the `usage` section of the `mdx` file. (again you can see
`button.stories.mdx` for an example).
## Potential Issues
There are some potential issues that you might encounter when writing a story
for a component. Please read the following sections carefully.
### Wrong Code Block Formatting
`mdx` is sensitive to whitespaces, so if you have a code block that has spaces
in between the code block, it will not render correctly, and you will get an
error. Always check the code block when you get an error.
For instance the following code block will generate the error (notice the extra
vertical space in the code block):
```jsx
export const TooltipTemplate = (args) => (
<div className="h-16 w-full">
<div class="w-8 h-8 mx-auto">
<Tooltip {...args}>
<Icon name="clipboard" />
</Tooltip>
// Imagine a whitespace here, we cannot show the extra vertical space, since
mdx will generate error
</div>
</div>
);
```
<WithImage image={CodeBlock} alt={'codeblock'} />
<br />
### Stories with the same name
You should not have multiple stories with the same name. If you have multiple
stories with the same name, it will cause an error.
The following code block will generate an error:
```jsx
<Canvas>
<Story name="Variants">
<Typography variant="h1" color="initial">
Heading 1
</Typography>
</Story>
</Canvas>
<Canvas>
<Story name="Variants">
<Typography variant="h4" color="initial">
Heading 4
</Typography>
</Story>
</Canvas>
```
@@ -0,0 +1,49 @@
import React from 'react';
export function createComponentTemplate(ComponentClass) {
return args => <ComponentClass {...args} />;
}
// const allowedIcons = iconsMetaData.reduce(
// (acc, icon) => {
// const Component = AllIcons[icon.file.split(".")[0]];
// acc.options.push(icon.name);
// acc.mapping[icon.name] = Component;
// return acc;
// },
// { options: [], mapping: {} }
// );
export function createStoryMetaSettings({
component,
enumPropNamesArray,
iconPropNamesArray,
}) {
const argTypes = {};
// set enum allowed values inside argsTypes object
enumPropNamesArray?.forEach(propName => {
const enums = component[`${propName}s`];
if (enums && enums instanceof Object) {
argTypes[propName] = {
options: Object.values(enums),
};
}
});
if (component === Avatar) console.log(argTypes);
// set icon allowed values inside argsTypes object
iconPropNamesArray?.forEach(propName => {
argTypes[propName] = {
options: allowedIcons.options,
mapping: allowedIcons.mapping,
control: {
type: 'select',
},
};
});
return argTypes;
}
@@ -0,0 +1,8 @@
import React from 'react';
import Header from '../assets/header.png';
export const WelcomeHeader = () => (
<div className="mb-8">
<img src={Header} alt="OHIF-v3 User Interface Library" />
</div>
);
@@ -0,0 +1,62 @@
import { Meta } from '@storybook/addon-docs';
import { WithImage } from './components';
import { WelcomeHeader } from './header/header';
import ComponentTree from './assets/component-tree.png';
import Button from './assets/button.png';
import Sandbox from './assets/sandbox.png';
import Code from './assets/code.png';
<Meta title="Welcome" />
<WelcomeHeader />
## Introduction
OHIF-v3 UI component library is a collection of components and utilities that
power OHIF's zero-footprint DICOM viewer ([demo](https://v3-demo.ohif.org/)). We maintain them as a separate
component library to:
- Decouple presentation from business logic
- Test and develop components isolation
- Provide well documented, reusable components
- Aid rapid application development for context specific viewers
<br />
## Storybook Introduction
We use [Storybook](https://storybook.js.org/) to document and showcase our
components. On the left side of the screen, you'll see the component tree
categorized into different sections.
<WithImage image={ComponentTree} alt="Component tree" className="my-8" />
### Getting Started
Each component folder contains examples of the component variants, its state and
a playground to test it. Note: we are actively working on the component variants
and their state and we are adding more examples as we go. Feel free to open a
pull request to add more examples.
<WithImage
image={Button}
alt="Button Component"
className="my-8 w-40 block mx-auto"
/>
### Sandbox
The Sandbox (Playground) section of each story is a live preview of the
component. You can test it by clicking on the component name in the component
tree. This section allows you to interact with the component and change its
properties.
<WithImage image={Sandbox} alt="Sandbox" className="my-8" />
### Code
Finally we have the code section. This section contains the source code of the
illustrated component. You can copy and paste the code to your own project.
<WithImage image={Code} alt="Code" className="my-8" />