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
No files matched your search
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
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" />
|
||||