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 2022-02-10 14:08:34 -05:00 committed by GitHub
parent eb77cb76b6
commit 5c8ee7f906
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 9004 additions and 2915 deletions

View File

@ -44,7 +44,7 @@
"react-resize-detector": "^3.4.0"
},
"dependencies": {
"@babel/runtime": "7.7.6",
"@babel/runtime": "7.16.3",
"lodash.merge": "^4.6.2",
"lodash.debounce": "4.0.8",
"react-cornerstone-viewport": "4.1.2"

View File

@ -41,6 +41,6 @@
"webpack-merge": "^5.7.3"
},
"dependencies": {
"@babel/runtime": "7.7.6"
"@babel/runtime": "7.16.3"
}
}

View File

@ -43,7 +43,7 @@
"react-cornerstone-viewport": "4.1.2"
},
"dependencies": {
"@babel/runtime": "7.7.6",
"@babel/runtime": "7.16.3",
"classnames": "^2.2.6"
}
}

View File

@ -40,7 +40,7 @@
"webpack-merge": "^5.7.3"
},
"dependencies": {
"@babel/runtime": "7.7.6",
"@babel/runtime": "7.16.3",
"@ohif/ui": "^1.8.2",
"@xstate/react": "^0.8.1",
"xstate": "^4.10.0"

View File

@ -32,7 +32,7 @@
"@ohif/core": "^2.12.3"
},
"dependencies": {
"@babel/runtime": "7.7.6"
"@babel/runtime": "7.16.3"
},
"devDependencies": {
"webpack": "^5.50.0",

View File

@ -52,8 +52,8 @@
"link-list": "npm ls --depth=0 --link=true"
},
"dependencies": {
"@babel/runtime": "7.7.6",
"config-point": "^0.3.4",
"@babel/runtime": "7.16.3",
"core-js": "^3.2.1",
"cornerstone-core": "2.6.0",
"wslink": "^0.1.8"
@ -134,6 +134,6 @@
]
},
"resolutions": {
"**/@babel/runtime": "7.7.6"
"**/@babel/runtime": "7.16.3"
}
}

View File

@ -38,7 +38,7 @@
"@ohif/ui": "^1.8.2"
},
"dependencies": {
"@babel/runtime": "7.7.6",
"@babel/runtime": "7.16.3",
"dcmjs": "0.16.1",
"dicomweb-client": "^0.6.0",
"isomorphic-base64": "^1.0.2",

View File

@ -80,7 +80,7 @@ the extension. The following lines of code should be added to the OHIF:
/* ... */
"dependencies": {
/* ... */
"@babel/runtime": "7.7.6",
"@babel/runtime": "7.16.3",
"@ohif/core": "^2.5.1",
"@ohif/extension-cornerstone": "^2.4.0",
"@ohif/extension-measurement-tracking": "^0.0.1",

View File

@ -36,7 +36,7 @@
"react-i18next": "^10.11.0"
},
"dependencies": {
"@babel/runtime": "7.7.6",
"@babel/runtime": "7.16.3",
"i18next-locize-backend": "^2.0.0",
"locize-editor": "^2.0.0",
"locize-lastused": "^1.1.0"

View File

@ -0,0 +1,8 @@
import { create } from '@storybook/theming';
export default create({
base: 'light',
brandTitle: 'OHIF',
brandUrl: 'https://ohif.org',
brandImage: 'ohif-logo-light.svg',
});

View File

@ -0,0 +1,3 @@
#storybook-explorer-menu svg {
color: #5034ff;
}

View File

@ -0,0 +1,62 @@
const path = require('path');
module.exports = {
stories: ['../src/**/*.stories.@(mdx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-google-analytics'],
core: {
builder: 'webpack5',
},
staticDirs: ['../static'],
webpackFinal: async (config, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
// config.module.rules[0].use[0].options.plugins[1] = [
// '@babel/plugin-proposal-class-properties',
// { loose: true },
// ];
// Make whatever fine-grained changes you need
config.module.rules.push({
test: /\.m?js/,
resolve: {
fullySpecified: false,
},
});
// Default rule for images /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
const fileLoaderRule = config.module.rules.find(
rule => rule.test && rule.test.test('.svg')
);
fileLoaderRule.exclude = /\.svg$/;
// console.log(JSON.stringify(config.module.rules, null, 2));
config.module.rules.push({
test: /\.svg$/,
use: [
{ loader: require.resolve('babel-loader') },
// { loader: 'svg-inline-loader' },
],
});
config.module.rules.push({
test: /\.css$/,
use: [
{
loader: 'postcss-loader',
options: {
// HERE: OPTIONS
postcssOptions: {
plugins: [require('tailwindcss'), require('autoprefixer')],
},
},
},
],
include: path.resolve(__dirname, '../'),
});
// Return the altered config
return config;
},
};

View File

@ -0,0 +1,28 @@
<link
href="https://fonts.googleapis.com/css?family=Lato:100,300,400,500,700&display=swap"
rel="stylesheet"
/>
<title>OHIF UI Component</title>
<style>
body {
font-family: 'Inter', sans-serif;
background: #f0f3ff !important;
font-size: 14px;
}
#storybook-explorer-menu svg {
color: #2b5282;
}
</style>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=G-3S63CTHNP6"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-3S63CTHNP6');
</script>

View File

@ -0,0 +1,17 @@
// .storybook/manager.js
import { addons } from '@storybook/addons';
import ohifTheme from './OHIFTheme';
import favicon from '/static/favicon.png';
const link = document.createElement('link');
link.setAttribute('rel', 'shortcut icon');
link.setAttribute('href', favicon);
document.head.appendChild(link);
addons.setConfig({
theme: ohifTheme,
});
window.STORYBOOK_GA_ID = "G-3S63CTHNP6"
window.STORYBOOK_REACT_GA_OPTIONS = {}

View File

@ -0,0 +1,84 @@
import { parameters } from '@storybook/addon-docs/dist/esm/frameworks/react/config';
import { addParameters } from '@storybook/react';
import { DocsPage, DocsContainer } from '@storybook/addon-docs/blocks';
import {
Heading,
SectionName,
Footer,
AnchorListItem,
LinkComponent,
} from '../src/storybook/components';
import '../src/tailwind.css';
import './custom.css';
// https://github.com/mondaycom/monday-ui-react-core/tree/master/.storybook
addParameters({
docs: {
...parameters.docs,
inlineStories: true,
container: ({ children, context }) => (
<DocsContainer context={context}>{children}</DocsContainer>
),
page: DocsPage,
components: {
Heading,
Footer,
h2: SectionName,
h3: ({ children }) => (
<h3 className="my-2 text-xl to-blue-900">{children}</h3>
),
li: AnchorListItem,
a: LinkComponent,
p: ({ children }) => (
<p className="my-2 text-gray-800 font-inter">{children}</p>
),
// todo: add pre and code
},
},
viewMode: 'docs',
previewTabs: {
'storybook/docs/panel': {
index: -1,
},
canvas: { title: 'Sandbox' },
},
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
viewport: {
disable: true,
},
backgrounds: {
default: 'OHIF-v3',
values: [
{
name: 'White',
value: '#FFFFFF',
},
{
name: 'OHIF-v3',
value: '#090C29',
},
{
name: 'Light',
value: '#F8F8F8',
},
{
name: 'Dark',
value: '#333333',
},
],
},
options: {
storySort: {
order: ['Welcome', 'Contribute', 'Foundations', 'Modals', '*'],
},
},
});
export const decorators = [];

View File

@ -1 +1,14 @@
# @ohif/ui
## For Running Storybook
```
cd platform/ui
yarn install
yarn storybook
```
Stories are available default at `http://localhost:6006/`

View File

@ -0,0 +1 @@
module.exports = require('../../babel.config.js');

View File

@ -21,7 +21,9 @@
"scripts": {
"start": "yarn run build --watch",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package": "yarn run build"
"build:package": "yarn run build",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"peerDependencies": {
"react": "17.0.2",
@ -44,7 +46,19 @@
"react-with-direction": "^1.3.1"
},
"devDependencies": {
"@babel/core": "^7.14.8",
"@storybook/addon-actions": "^6.4.9",
"@storybook/addon-docs": "^6.4.9",
"@storybook/addon-essentials": "^6.4.9",
"@storybook/addon-google-analytics": "^6.2.9",
"@storybook/addon-links": "^6.4.9",
"@storybook/builder-webpack5": "^6.4.9",
"@storybook/manager-webpack5": "^6.4.9",
"@storybook/react": "^6.4.9",
"@storybook/source-loader": "^6.4.9",
"autoprefixer": "9.7.4",
"babel-loader": "^8.2.2",
"dotenv-webpack": "6.0.4",
"postcss": "^8.3.5",
"postcss-loader": "^6.1.1",
"prop-types": "15.7.2",

View File

@ -13,7 +13,7 @@ const Link = ({ href, children, showIcon = false }) => {
>
{children}
{!!showIcon && (
<Icon name="external-link" className="w-5 text-white ml-2" />
<Icon name="external-link" className="w-5 ml-2 text-white" />
)}
</Typography>
</a>
@ -23,31 +23,31 @@ const Link = ({ href, children, showIcon = false }) => {
const Row = ({ title, value, link }) => {
return (
<div className="flex mb-4">
<Typography variant="subtitle" component="p" className="text-white w-48">
<Typography variant="subtitle" component="p" className="w-48 text-white">
{title}
</Typography>
{link ? (
<Link href={link}>{value}</Link>
) : (
<Typography
variant="subtitle"
component="p"
className="text-white w-48"
>
{value}
</Typography>
)}
<Typography
variant="subtitle"
component="p"
className="w-48 text-white"
>
{value}
</Typography>
)}
</div>
);
};
const AboutModal = ({buildNumber, versionNumber}) => {
const AboutModal = ({ buildNumber, versionNumber }) => {
const { os, version, name } = detect();
const browser = `${name[0].toUpperCase()}${name.substr(1)} ${version}`;
const renderRowTitle = title => (
<div className="border-b-2 border-black pb-3 mb-3">
<div className="pb-3 mb-3 border-b-2 border-black">
<Typography variant="h6" className="text-primary-light">
{title}
</Typography>
@ -57,10 +57,7 @@ const AboutModal = ({buildNumber, versionNumber}) => {
<div>
{renderRowTitle('Important Links')}
<div className="flex mb-8">
<Link
href="https://community.ohif.org/"
showIcon={true}
>
<Link href="https://community.ohif.org/" showIcon={true}>
Visit the forum
</Link>
<span className="ml-4">
@ -102,6 +99,6 @@ const AboutModal = ({buildNumber, versionNumber}) => {
AboutModal.propTypes = {
buildNumber: PropTypes.string,
versionNumber: PropTypes.string,
}
};
export default AboutModal;

View File

@ -0,0 +1,48 @@
import AboutModal from '../AboutModal';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import {
createComponentTemplate,
createStoryMetaSettings,
} from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: AboutModal,
title: 'Modals/About',
};
<Meta title="Modals/About" component={AboutModal} />
export const aboutTemplate = args => (
<div className="bg-primary-dark">
<AboutModal {...args} />
</div>
);
<Heading
title="About Modal"
componentRelativePath="AboutModal/AboutModal.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
OHIF about modal component provides information about the application version
and build number
<Canvas>
<Story name="Overview">{aboutTemplate.bind({})}</Story>
</Canvas>
## Props
<ArgsTable of={AboutModal} />
## Usage
## Contribute
<Footer componentRelativePath="AboutModal/__stories__/aboutModal.stories.mdx" />

View File

@ -23,7 +23,6 @@ const roundedClasses = {
full: 'rounded-full',
};
const variantClasses = {
text: {
default:
@ -64,16 +63,11 @@ const variantClasses = {
'border bg-primary-light border-primary-light text-black hover:opacity-80 active:opacity-100 focus:opacity-80',
},
disabled: {
default:
'cursor-not-allowed opacity-50 bg-primary-light text-black',
primary:
'cursor-not-allowed opacity-50 bg-primary-main text-white',
secondary:
'cursor-not-allowed opacity-50 bg-secondary-light text-white',
white:
'cursor-not-allowed opacity-50 bg-white text-black',
black:
'cursor-not-allowed opacity-50 bg-black text-white',
default: 'cursor-not-allowed opacity-50 bg-primary-light text-black',
primary: 'cursor-not-allowed opacity-50 bg-primary-main text-white',
secondary: 'cursor-not-allowed opacity-50 bg-secondary-light text-white',
white: 'cursor-not-allowed opacity-50 bg-white text-black',
black: 'cursor-not-allowed opacity-50 bg-black text-white',
light:
'cursor-not-allowed opacity-50 border bg-primary-light border-primary-light text-black',
},
@ -140,7 +134,7 @@ const Button = ({
variantClasses[variant][color],
roundedClasses[rounded],
sizeClasses[size],
fullWidthClasses[fullWidth],
fullWidthClasses[fullWidth]
)}
disabled={disabled}
ref={buttonElement}
@ -156,11 +150,25 @@ const Button = ({
);
};
Button.defaultProps = {
color: defaults.color,
disabled: false,
children: '',
onClick: () => {},
};
Button.propTypes = {
/** What is inside the button, can be text or react component */
children: PropTypes.node,
/** Callback to be called when the button is clicked */
onClick: PropTypes.func.isRequired,
/** Button size */
size: PropTypes.oneOf(['small', 'medium', 'large', 'initial', 'inherit']),
/** Button corner roundness */
rounded: PropTypes.oneOf(['none', 'small', 'medium', 'large', 'full']),
/** Button variants */
variant: PropTypes.oneOf(['text', 'outlined', 'contained']),
/** Button color */
color: PropTypes.oneOf([
'default',
'primary',
@ -168,16 +176,21 @@ Button.propTypes = {
'white',
'black',
'inherit',
'light'
'light',
]),
/** Whether the button should have full width */
fullWidth: PropTypes.bool,
/** Whether the button should be disabled */
disabled: PropTypes.bool,
/** Button type */
type: PropTypes.string,
name: PropTypes.string,
/** Button start icon name - if any icon is specified */
startIcon: PropTypes.node,
/** Button end icon name - if any icon is specified */
endIcon: PropTypes.node,
/** Additional TailwindCSS classnames */
className: PropTypes.string,
onClick: PropTypes.func.isRequired,
};
export default Button;

View File

@ -0,0 +1,170 @@
import Button from '../Button';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import {
createComponentTemplate,
createStoryMetaSettings,
} from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Button,
title: 'Components/Button',
};
<Meta title="Components/Button" component={Button} />
export const buttonTemplate = createComponentTemplate(Button);
<Heading title="Button" componentRelativePath="Button/Button.jsx"/>
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
You can use the button component to create a button. It can be used in different ways, the default
button is a simple button with a text.
<Canvas>
<Story name="Overview"
args={{children: "Button", color:"default"}}>
{buttonTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Button} />
## Usage
### Variants
There can be different variants of buttons: `text`, `outlined`,
`contained`, and `disabled`.
<Canvas>
<Story name="Variants" >
<div className="flex space-x-2">
<Button variant="text">Text Button</Button>
<Button variant="outlined">Outlined Button</Button>
<Button variant="contained">Contained Button</Button>
<Button variant="disabled">Disabled Button</Button>
</div>
</Story>
</Canvas>
### Colors
There are different colors for the button: `default`, `primary`, `secondary`, `white`, `black`, `inherit`, `light`.
<Canvas>
<Story name="Colors" >
<div className="flex space-x-2">
<Button color="default">Default Button</Button>
<Button color="primary">Primary Button</Button>
<Button color="secondary">Secondary Button</Button>
<Button color="white">White Button</Button>
<Button color="black">Black Button</Button>
<Button color="inherit">Inherit Button</Button>
<Button color="light">Light Button</Button>
</div>
</Story>
</Canvas>
### Mixing props
You can mix different props together to create a button.
<Canvas>
<Story name="Custom" >
<Button color="secondary" variant="outlined">Custom Button</Button>
</Story>
</Canvas>
### Disabled
You can disable the button by setting the variant to `disabled`.
<Canvas>
<Story name="Disabled" >
<Button variant="disabled">Disabled Button</Button>
</Story>
</Canvas>
### Start/End Icons
You can add an icon to the start of the button. It accepts an icon component.
<Canvas>
<Story name="Start Icon" >
{() => {
// svg icon for github
const Github = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="feather feather-github"
>
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
</svg>
)
}
return <Button startIcon={<Github />}>Start Icon Button</Button>;
}}
}}
</Story>
</Canvas>
End Icon is the same as start icon, but for the end of the button.
<Canvas>
<Story name="End Icon" >
{() => {
// svg icon for github
const Github = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="feather feather-github"
>
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
</svg>
)
}
return <Button startIcon={<Github />} endIcon={<Github />}>Start and End Icon Button</Button>;
}}
}}
</Story>
</Canvas>
### Full width
You can make the button full width by setting the `fullWidth` prop to `true`.
<Canvas>
<Story name="Full Width" >
<Button fullWidth color="secondary">Full Width Button</Button>
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="Button/__stories__/button.stories.mdx"/>

View File

@ -0,0 +1,101 @@
import ButtonGroup from '../ButtonGroup';
import Button from '../../Button';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import {
createComponentTemplate,
createStoryMetaSettings,
} from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: ButtonGroup,
title: 'Components/ButtonGroup',
};
<Meta title="Components/ButtonGroup" component={ButtonGroup} />
export const buttonGroupTemplate = args => (
<ButtonGroup {...args}>
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
);
<Heading
title="ButtonGroup"
componentRelativePath="ButtonGroup/ButtonGroup.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
ButtonGroup is a container for a group of buttons.
<Canvas>
<Story name="Overview">{buttonGroupTemplate.bind({})}</Story>
</Canvas>
## Props
<ArgsTable of={ButtonGroup} />
## Usage
### Variants
There are three different variants of ButtonGroup:
<Canvas>
<Story name="Variants">
<div className="flex flex-col space-y-2">
<ButtonGroup variant="text">
<Button>Text</Button>
<Button>Text</Button>
<Button>Text</Button>
</ButtonGroup>
<ButtonGroup variant="outlined">
<Button>Outlined</Button>
<Button>Outlined</Button>
<Button>Outlined</Button>
</ButtonGroup>
<ButtonGroup variant="contained">
<Button>Contained</Button>
<Button>Contained</Button>
<Button>Contained</Button>
</ButtonGroup>
</div>
</Story>
</Canvas>
### Orientation
You can change the orientation of the buttons in the group by setting the
`orientation` prop.
<Canvas>
<Story name="Orientation Horizontal">
<ButtonGroup orientation="horizontal">
<Button>Horizontal-1</Button>
<Button>Horizontal-2</Button>
</ButtonGroup>
</Story>
</Canvas>
Or vertical
<Canvas>
<Story name="Orientation Vertical">
<ButtonGroup orientation="vertical">
<Button>Vertical-1</Button>
<Button>Vertical-2</Button>
</ButtonGroup>
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="ButtonGroup/__stories__/buttonGroup.stories.mdx" />

View File

@ -0,0 +1,32 @@
import CinePlayer from '../CinePlayer';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: CinePlayer,
title: 'Components/CinePlayer',
};
<Meta title="Components/CinePlayer" component={CinePlayer} />
export const cineTemplate = createComponentTemplate(CinePlayer);
<Heading title="CinePlayer" componentRelativePath="CinePlayer/CinePlayer.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
<Canvas>
<Story name="Overview">{cineTemplate.bind({})}</Story>
</Canvas>
## Props
<ArgsTable of={CinePlayer} />
## Contribute
<Footer componentRelativePath="CinePlayer/__stories__/cinePlayer.stories.mdx" />

View File

@ -0,0 +1,63 @@
import ContextMenu from '../ContextMenu';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: ContextMenu,
title: 'Modals/ContextMenu',
};
<Meta title="Modals/ContextMenu" component={ContextMenu} />
export const contextMenueTemplate = createComponentTemplate(ContextMenu);
<Heading
title="ContextMenu"
componentRelativePath="ContextMenu/ContextMenu.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
Context Menu is a component that is used to display a list of options to the
user. This component can be used for use cases such as opening a list of options
on user right click.
<Canvas>
<Story
name="Overview"
args={{
items: [
{
label: 'Delete measurement',
actionType: 'Delete',
action: (item) => {
window.alert(`${item.label} clicked`);
},
value: {},
},
{
label: 'Add Label',
actionType: 'setLabel',
action: (item) => {
window.alert(`${item.label} clicked`);
},
value: {},
},
],
}}
>
{contextMenueTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={ContextMenu} />
## Contribute
<Footer componentRelativePath="ContextMenu/__stories__/contextMenu.stories.mdx" />

View File

@ -0,0 +1,46 @@
import DateRange from '../DateRange';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
export const argTypes = {
component: DateRange,
title: 'Components/DateRange',
};
<Meta title="Components/DateRange" component={DateRange} />
export const DateRangeTemplate = (args) => (
<div className="h-96">
<DateRange {...args} />
</div>
);
<Heading title="DateRange" componentRelativePath="DateRange/DateRange.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
DateRange is a component that allows you to select a range of dates.
<Canvas>
<Story
name="Overview"
args={{
id: 'date-range-1',
startDate: '1990-05-01',
endDate: '2022-01-01',
}}
>
{DateRangeTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={DateRange} />
## Contribute
<Footer componentRelativePath="DateRange/__stories__/dateRange.stories.mdx" />

View File

@ -0,0 +1,67 @@
import Dialog from '../Dialog';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Dialog,
title: 'Modals/Dialog',
};
<Meta title="Modals/Dialog" component={Dialog} />
export const DialogTemplate = createComponentTemplate(Dialog);
<Heading title="Dialog" componentRelativePath="Dialog/Dialog.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
Dialog is a modal dialog component which enabled to show a modal dialog.
<Canvas>
<Story name="Overview">{DialogTemplate.bind({})}</Story>
</Canvas>
## Props
<ArgsTable of={Dialog} />
## Usage
It can be used to show a modal dialog to submit a form or show a message.
<Canvas>
<Story
name="Submit"
args={{
title: 'Dialog Title',
text: 'Dialog Text',
onClose: () => {
window.alert('Dialog closed');
},
noCloseButton: false,
actions: [
{
id: 'cancel',
text: 'Cancel',
type: 'cancel',
},
{
id: 'submit',
text: 'Submit',
type: 'primary',
},
],
}}
>
{DialogTemplate.bind({})}
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="Dialog/__stories__/dialog.stories.mdx" />

View File

@ -21,7 +21,7 @@ const Dropdown = ({ id, children, showDropdownIcon, list }) => {
}}
data-cy={id}
>
{!!icon && <Icon name={icon} className="text-white w-4 mr-2" />}
{!!icon && <Icon name={icon} className="w-4 mr-2 text-white" />}
<Typography>{title}</Typography>
</div>
);
@ -40,11 +40,9 @@ const Dropdown = ({ id, children, showDropdownIcon, list }) => {
const renderTitleElement = () => {
return (
<div className="flex text-white items-center">
<div className="flex items-center">
{children}
{showDropdownIcon && (
<Icon name="chevron-down" className="text-white ml-1" />
)}
{showDropdownIcon && <Icon name="chevron-down" className="ml-1" />}
</div>
);
};
@ -94,7 +92,7 @@ const Dropdown = ({ id, children, showDropdownIcon, list }) => {
return (
<div data-cy="dropdown" ref={element} className="relative">
<div className="cursor-pointer flex items-center" onClick={toggleList}>
<div className="flex items-center cursor-pointer" onClick={toggleList}>
{renderTitleElement()}
</div>

View File

@ -0,0 +1,75 @@
import Dropdown from '../Dropdown';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Dropdown,
title: 'Components/Dropdown',
};
<Meta title="Components/Dropdown" component={Dropdown} />
export const DropdownTemplate = (args) => (
// Todo: this should not set a background
<div className="flex h-32">
<Dropdown {...args} />
</div>
);
<Heading title="Dropdown" componentRelativePath="Dropdown/Dropdown.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
Dropdown is a modal Dropdown component which enabled to show a modal Dropdown.
<Canvas>
<Story name="Overview" args={{ id: 'dropdown-d', list: [] }}>
{DropdownTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Dropdown} />
## Usage
You can list the items in the dropdown.
<Canvas>
<Story
name="List"
args={{
id: 'dropdown-1',
children: <div className="text-black">Drop Down</div>,
showDropdownIcon: true,
list: [
{
title: 'Item 1',
icon: 'clipboard',
onClick: () => {
alert('Item 1 clicked');
},
},
{
title: 'Item 2',
icon: 'tracked',
onClick: () => {
alert('Item 2 clicked');
},
},
],
}}
>
{DropdownTemplate.bind({})}
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="Dropdown/__stories__/dropdown.stories.mdx" />

View File

@ -14,7 +14,7 @@ const ExpandableToolbarButton = ({
icon,
className,
content: Content,
contentProps
contentProps,
}) => {
const classes = {
type: {
@ -54,7 +54,12 @@ const ExpandableToolbarButton = ({
<Icon name={icon} />
</IconButton>
<div className="absolute z-10 pt-4">
<div className={classnames("ExpandableToolbarButton__content w-48", className)}>
<div
className={classnames(
'ExpandableToolbarButton__content w-48',
className
)}
>
<Content {...contentProps} onClick={onChildClickHandler} />
</div>
</div>
@ -62,9 +67,11 @@ const ExpandableToolbarButton = ({
);
};
const noop = () => { };
const noop = () => {};
ExpandableToolbarButton.defaultProps = {
id: '',
icon: 'clipboard',
isActive: false,
type: 'primary',
content: null,

View File

@ -0,0 +1,51 @@
import Header from '../Header';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Header,
title: 'Components/Header',
};
<Meta title="Components/Header" component={Header} />
export const HeaderTemplate = createComponentTemplate(Header);
<Heading title="Header" componentRelativePath="Header/Header.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
Header is a component that renders the header of the application.
<Canvas>
<Story
name="Overview"
args={{
menuOptions: [
{
title: 'About',
icon: 'info',
onClick: () => window.alert('About clicked'),
},
],
isReturnEnabled: true,
isSticky: false,
onClickReturnButton: () => window.alert('Return clicked'),
}}
>
{HeaderTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Header} />
## Contribute
<Footer componentRelativePath="Header/__stories__/header.stories.mdx" />

View File

@ -4,12 +4,17 @@ import classNames from 'classnames';
import { HotkeyField, Typography } from '../';
import { useTranslation } from 'react-i18next';
/* TODO: Move these configs and utils to core? */
import { MODIFIER_KEYS } from './hotkeysConfig';
import { validate, splitHotkeyDefinitionsAndCreateTuples } from './utils';
const HotkeysPreferences = ({ disabled, hotkeyDefinitions, errors: controlledErrors, onChange, hotkeysModule }) => {
const HotkeysPreferences = ({
disabled,
hotkeyDefinitions,
errors: controlledErrors,
onChange,
hotkeysModule,
}) => {
const { t } = useTranslation('UserPreferencesModal');
const visibleHotkeys = Object.keys(hotkeyDefinitions)
@ -42,51 +47,74 @@ const HotkeysPreferences = ({ disabled, hotkeyDefinitions, errors: controlledErr
};
return (
<div className='flex flex-row justify-center'>
<div className='flex flex-row justify-evenly w-full'>
<div className="flex flex-row justify-center">
<div className="flex flex-row w-full justify-evenly">
{splitedHotkeys.map((hotkeys, index) => {
return (
<div key={`HotkeyGroup@${index}`} className='flex flex-row'>
<div className='p-2 text-right flex flex-col'>
<div key={`HotkeyGroup@${index}`} className="flex flex-row">
<div className="flex flex-col p-2 text-right">
{hotkeys.map((hotkey, hotkeyIndex) => {
const [id, definition] = hotkey;
const isFirst = hotkeyIndex === 0;
const error = errors[id];
const onChangeHandler = keys => onHotkeyChangeHandler(id, { ...definition, keys });
const onChangeHandler = keys =>
onHotkeyChangeHandler(id, { ...definition, keys });
return (
<div key={`HotkeyItem@${hotkeyIndex}`} className='flex flex-row justify-end mb-2'>
<div className='flex flex-col items-center'>
<div
key={`HotkeyItem@${hotkeyIndex}`}
className="flex flex-row justify-end mb-2"
>
<div className="flex flex-col items-center">
<Typography
variant='subtitle'
className={classNames('pr-6 w-full text-right text-primary-light', !isFirst && 'hidden')}
variant="subtitle"
className={classNames(
'pr-6 w-full text-right text-primary-light',
!isFirst && 'hidden'
)}
>
{t('Function')}
</Typography>
</Typography>
<Typography
variant='subtitle'
className={classNames('pr-6 h-full flex flex-row items-center whitespace-nowrap', isFirst && 'mt-5')}>
variant="subtitle"
className={classNames(
'pr-6 h-full flex flex-row items-center whitespace-nowrap',
isFirst && 'mt-5'
)}
>
{definition.label}
</Typography>
</div>
<div className='flex flex-col'>
<div className="flex flex-col">
<Typography
variant='subtitle'
className={classNames('pr-6 pl-0 text-left text-primary-light', !isFirst && 'hidden')}
variant="subtitle"
className={classNames(
'pr-6 pl-0 text-left text-primary-light',
!isFirst && 'hidden'
)}
>
{t('Shortcut')}
</Typography>
<div className={classNames('flex flex-col w-32', isFirst && 'mt-5')}>
</Typography>
<div
className={classNames(
'flex flex-col w-32',
isFirst && 'mt-5'
)}
>
<HotkeyField
disabled={disabled}
keys={definition.keys}
modifierKeys={MODIFIER_KEYS}
onChange={onChangeHandler}
hotkeys={hotkeysModule}
className='text-lg h-8'
className="h-8 text-lg"
/>
{error && <span className='p-2 text-left text-red-600 text-sm'>{error}</span>}
{error && (
<span className="p-2 text-sm text-left text-red-600">
{error}
</span>
)}
</div>
</div>
</div>
@ -101,7 +129,7 @@ const HotkeysPreferences = ({ disabled, hotkeyDefinitions, errors: controlledErr
);
};
const noop = () => { };
const noop = () => {};
HotkeysPreferences.propTypes = {
onChange: PropTypes.func,
@ -113,12 +141,12 @@ HotkeysPreferences.propTypes = {
unpause: PropTypes.func.isRequired,
startRecording: PropTypes.func.isRequired,
record: PropTypes.func.isRequired,
}).isRequired
}).isRequired,
};
HotkeysPreferences.defaultProps = {
onChange: noop,
disabled: false
disabled: false,
};
export default HotkeysPreferences;

View File

@ -2,12 +2,21 @@ import React from 'react';
import PropTypes from 'prop-types';
import getIcon from './getIcon';
const Icon = ({ name, ...otherProps }) => {
const Icon = ({ name, className, ...otherProps }) => {
if (className) {
return (
<div className={className}>
{getIcon(name, className, { ...otherProps })}
</div>
);
}
return <React.Fragment>{getIcon(name, { ...otherProps })}</React.Fragment>;
};
Icon.propTypes = {
name: PropTypes.string.isRequired,
className: PropTypes.string,
};
export default Icon;

View File

@ -0,0 +1,92 @@
import Icon from '../Icon';
import { ICONS } from '../getIcon';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Icon,
title: 'Components/Icon',
};
<Meta title="Components/Icon" component={Icon} />
export const IconTemplate = (args) => (
// Todo: Icon colors
<div className="w-8 h-8">
<Icon {...args} />
</div>
);
<Heading title="Icon" componentRelativePath="Icon/Icon.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
Icon is a component that renders the Icons.
<Canvas>
<Story
name="Overview"
args={{
name: 'clipboard',
}}
>
{IconTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Icon} />
## Usage
You can choose the icon from the list of icons. Here we have render all the
icons.
<Canvas>
<Story name="All Icons">
{() => {
const icons = Object.keys(ICONS);
return (
<div className="flex flex-wrap">
{icons
.filter((ic) => ic !== 'magnifier')
.map((icon) => (
<div className="m-4 flex flex-col justify-center items-center">
<div class="w-8 h-8">
<Icon name={icon} />
</div>
<div className="text-gray-600">{icon}</div>
</div>
))}
</div>
);
}}
</Story>
</Canvas>
## Color
Icon colors can be changed by wrapping the icon in a div with the class name
<Canvas>
<Story
name="Colors"
args={{
name: 'clipboard',
className: 'text-red-700',
}}
>
{IconTemplate.bind({})}
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="Icon/__stories__/icon.stories.mdx" />

View File

@ -0,0 +1,26 @@
import React from 'react';
import IconButton from './IconButton';
import Icon from '../Icon/Icon';
import { ICONS } from '../Icon/getIcon';
export default {
component: IconButton,
title: 'Icons/IconButton',
argTypes: {
iconName: {
control: { type: 'select', options: Object.keys(ICONS) },
},
},
};
const Template = ({ iconName, ...args }) => (
<IconButton {...args}>
<Icon name={iconName} />
</IconButton>
);
export const Default = Template.bind({});
Default.args = {
iconName: 'clipboard',
};

View File

@ -0,0 +1,75 @@
import Input from '../Input';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Input,
title: 'Components/Input',
};
<Meta title="Components/Input" component={Input} />
export const InputTemplate = createComponentTemplate(Input);
<Heading title="Input" componentRelativePath="Input/Input.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
Input is a component that renders the Inputs.
<Canvas>
<Story
name="Overview"
args={{
label: 'Input',
}}
>
{InputTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Input} />
## Usage
### Transparent
<Canvas>
<Story
name="Transparent"
args={{
transparent: true,
}}
>
{InputTemplate.bind({})}
</Story>
</Canvas>
### Classnames
You can change the appearance of the container and label.
<Canvas>
<Story
name="ClassNames"
args={{
label: 'Input',
containerClassName: 'bg-gray-500',
labelClassName: 'text-yellow-500',
}}
>
{InputTemplate.bind({})}
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="Input/__stories__/input.stories.mdx" />

View File

@ -0,0 +1,38 @@
import LayoutSelector from '../LayoutSelector';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: LayoutSelector,
title: 'Components/LayoutSelector',
};
<Meta title="Components/LayoutSelector" component={LayoutSelector} />
export const LayoutSelectorTemplate = createComponentTemplate(LayoutSelector);
<Heading
title="LayoutSelector"
componentRelativePath="LayoutSelector/LayoutSelector.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
LayoutSelector is a component that renders the LayoutSelectors.
<Canvas>
<Story name="Overview">{LayoutSelectorTemplate.bind({})}</Story>
</Canvas>
## Props
<ArgsTable of={LayoutSelector} />
## Contribute
<Footer componentRelativePath="LayoutSelector/__stories__/layoutSelector.stories.mdx" />

View File

@ -0,0 +1,73 @@
import MeasurementTable from '../MeasurementTable';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: MeasurementTable,
title: 'Components/MeasurementTable',
};
<Meta title="Components/MeasurementTable" component={MeasurementTable} />
export const MeasurementTableTemplate = (args) => (
<div className="w-64 h-64 bg-primary-dark">
<MeasurementTable {...args} />
</div>
);
<Heading
title="MeasurementTable"
componentRelativePath="MeasurementTable/MeasurementTable.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
MeasurementTable is a component that renders the MeasurementTables.
<Canvas>
<Story
name="Overview"
args={{
title: 'Measurements',
}}
>
{MeasurementTableTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={MeasurementTable} />
## Usage
With data
<Canvas>
<Story
name="With Data"
args={{
title: 'Measurements',
amount: 1,
data: [
{
id: 1,
label: '(empty)',
displayText: ['193.1 mm (S:6, I:5)'],
isActive: false,
},
],
}}
>
{MeasurementTableTemplate.bind({})}
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="MeasurementTable/__stories__/measurementTable.stories.mdx" />

View File

@ -3,7 +3,14 @@ import classnames from 'classnames';
import PropTypes from 'prop-types';
import { Button, Icon } from '../';
const Notification = ({ id, type, message, actions, onSubmit, onOutsideClick }) => {
const Notification = ({
id,
type,
message,
actions,
onSubmit,
onOutsideClick,
}) => {
const notificationRef = useRef(null);
useEffect(() => {
@ -93,7 +100,7 @@ Notification.defaultProps = {
};
Notification.propTypes = {
type: PropTypes.string,
type: PropTypes.oneOf(['error', 'warning', 'info', 'success']),
message: PropTypes.string.isRequired,
actions: PropTypes.arrayOf(
PropTypes.shape({

View File

@ -0,0 +1,63 @@
import Notification from '../Notification';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Notification,
title: 'Components/Notification',
};
<Meta title="Components/Notification" component={Notification} />
export const NotificationTemplate = createComponentTemplate(Notification);
<Heading
title="Notification"
componentRelativePath="Notification/Notification.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
Notification is a component that renders the Notifications.
<Canvas>
<Story
name="Overview"
args={{
message: 'Track all measurement for this series?',
type: 'info',
actions: [
{
type: 'cancel',
text: 'No',
value: 0,
},
{
type: 'secondary',
text: 'No, do not ask again',
value: -1,
},
{
type: 'primary',
text: 'Yes',
value: 1,
},
],
}}
>
{NotificationTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Notification} />
## Contribute
<Footer componentRelativePath="Notification/__stories__/notification.stories.mdx" />

View File

@ -0,0 +1,71 @@
import SegmentationTable from '../SegmentationTable';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: SegmentationTable,
title: 'Components/SegmentationTable',
};
<Meta title="Components/SegmentationTable" component={SegmentationTable} />
export const SegmentationTableTemplate = (args) => (
<div className="w-64 h-96 bg-primary-dark">
<SegmentationTable {...args} />
</div>
);
<Heading
title="SegmentationTable"
componentRelativePath="SegmentationTable/SegmentationTable.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
SegmentationTable is a component that renders the SegmentationTables.
<Canvas>
<Story
name="Overview"
args={{
title: 'Segmentations',
}}
>
{SegmentationTableTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={SegmentationTable} />
## Usage
<Canvas>
<Story
name="With Data"
args={{
title: 'Segmentations',
amount: 1,
activeSegmentationId: 'ptScene-labelmap-0',
segmentations: [
{
id: 'ptScene-labelmap-0',
label: 'ptScene-labelmap-0',
displayText: ['SUV Peak: 25.58'],
},
],
}}
>
{SegmentationTableTemplate.bind({})}
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="SegmentationTable/__stories__/segmentationTable.stories.mdx" />

View File

@ -0,0 +1,58 @@
import SidePanel from '../SidePanel';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: SidePanel,
title: 'Components/SidePanel',
};
<Meta title="Components/SidePanel" component={SidePanel} />
export const SidePanelTemplate = (args) => (
<div className="w-96">
<SidePanel {...args} />
</div>
);
<Heading title="SidePanel" componentRelativePath="SidePanel/SidePanel.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
SidePanel is a component that renders the SidePanels.
<Canvas>
<Story
name="Overview"
args={{
side: 'left',
defaultComponentOpen: 'Example',
childComponents: [
{
iconName: 'home',
iconLabel: 'launch-info',
name: 'Example',
label: 'home',
content: () => <div>Hello</div>,
},
],
}}
>
{SidePanelTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={SidePanel} />
## Usage
## Contribute
<Footer componentRelativePath="SidePanel/__stories__/sidePanel.stories.mdx" />

View File

@ -0,0 +1,75 @@
import SplitButton from '../SplitButton';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: SplitButton,
title: 'Components/SplitButton',
};
<Meta title="Components/SplitButton" component={SplitButton} />
export const SplitButtonTemplate = (args) => (
<div className="h-32 bg-primary-dark">
<SplitButton {...args} />
</div>
);
<Heading
title="SplitButton"
componentRelativePath="SplitButton/SplitButton.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
SplitButton is a component that renders the SplitButtons.
<Canvas>
<Story
name="Overview"
args={{
primary: {
id: 'Length',
icon: 'tool-length',
label: 'Length',
type: 'tool',
commandName: undefined,
commandOptions: { toolName: 'Length' },
tooltip: 'Length',
},
bState: {},
secondary: {
icon: 'chevron-down',
label: '',
isActive: true,
tooltip: 'More Measure Tools',
},
items: [
{
id: 'Bidirectional',
icon: 'tool-bidirectional',
label: 'Bidirectional',
type: 'tool',
commandName: undefined,
commandOptions: { toolName: 'Bidirectional' },
tooltip: 'Bidirectional',
},
],
}}
>
{SplitButtonTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={SplitButton} />
## Contribute
<Footer componentRelativePath="SplitButton/__stories__/splitButton.stories.mdx" />

View File

@ -0,0 +1,129 @@
import StudyBrowser from '../StudyBrowser';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: StudyBrowser,
title: 'Components/StudyBrowser',
};
<Meta title="Components/StudyBrowser" component={StudyBrowser} />
export const StudyBrowserTemplate = (args) => (
<div className="w-96">
<StudyBrowser {...args} />
</div>
);
<Heading
title="StudyBrowser"
componentRelativePath="StudyBrowser/StudyBrowser.jsx"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
StudyBrowser is a component that renders the StudyBrowsers.
<Canvas>
<Story
name="Overview"
args={{
tabs: [
{
name: 'primary',
label: 'Primary',
studies: [],
},
{
name: 'recent',
label: 'Recent',
studies: [],
},
{
name: 'all',
label: 'All',
studies: [],
},
],
expandedStudyInstanceUIDs: ['1.3.6.1.4'],
activeTabName: 'primary',
}}
>
{StudyBrowserTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={StudyBrowser} />
## Usage
With Studies
<Canvas>
<Story
name="With Studies"
args={{
tabs: [
{
name: 'primary',
label: 'Primary',
studies: [
{
studyInstanceUid:
'1.3.6.1.4.1.25403.345050719074.3824.20170126085406.1',
date: '26-Oct-2015',
description: 'CT CHEST W CONTRAST',
modalities: 'CT',
numInstances: 1,
displaySets: [
{
displaySetInstanceUID: '0b66675a-a4e3-a718-21f4-035d2d178d62',
description: 'Topogram 0.6',
seriesNumber: '1',
modality: 'CT',
seriesDate: '26-Oct-2015',
numInstances: 1,
StudyInstanceUID:
'1.3.6.1.4.1.25403.345050719074.3824.20170126085406.1',
componentType: 'thumbnailTracked',
dragData: {
type: 'displayset',
displaySetInstanceUID:
'0b66675a-a4e3-a718-21f4-035d2d178d62',
},
isTracked: false,
viewportIdentificator: [],
},
],
},
],
},
{
name: 'recent',
label: 'Recent',
studies: [],
},
{
name: 'all',
label: 'All',
studies: [],
},
],
expandedStudyInstanceUIDs: ['1.3.6.1.4'],
activeTabName: 'primary',
}}
>
{StudyBrowserTemplate.bind({})}
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="StudyBrowser/__stories__/studyBrowser.stories.mdx" />

View File

@ -0,0 +1,52 @@
import StudyItem from '../StudyItem';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: StudyItem,
title: 'Components/StudyItem',
};
<Meta title="Components/StudyItem" component={StudyItem} />
export const StudyItemTemplate = (args) => (
<div className="w-96">
<StudyItem {...args} />
</div>
);
<Heading title="StudyItem" componentRelativePath="StudyItem/StudyItem.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
StudyItem is a component that renders the StudyItems.
<Canvas>
<Story
name="Overview"
args={{
date: '16-Jan-1960',
description: 'WB PET/CT',
numInstances: 934,
modalities: 'CTPT',
isActive: false,
}}
>
{StudyItemTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={StudyItem} />
## Usage
## Contribute
<Footer componentRelativePath="StudyItem/__stories__/studyItem.stories.mdx" />

View File

@ -0,0 +1,57 @@
import StudyListExpandedRow from '../StudyListExpandedRow';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: StudyListExpandedRow,
title: 'Components/StudyListExpandedRow',
};
<Meta
title="Components/StudyListExpandedRow"
component={StudyListExpandedRow}
/>
export const StudyListExpandedRowTemplate =
createComponentTemplate(StudyListExpandedRow);
<Heading
title="StudyListExpandedRow"
componentRelativePath="StudyListExpandedRow/StudyListExpandedRow.js"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
StudyListExpandedRow is a component that renders the StudyListExpandedRows.
<Canvas>
<Story
name="Overview"
args={{
seriesTableColumns: {
description: 'Description',
seriesNumber: 'Series',
modality: 'Modality',
instances: 'Instances',
},
seriesTableDataSource: [],
}}
>
{StudyListExpandedRowTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={StudyListExpandedRow} />
## Usage
## Contribute
<Footer componentRelativePath="StudyListExpandedRow/__stories__/studyListExpandedRow.stories.mdx" />

View File

@ -0,0 +1,66 @@
import ToolbarButton from '../ToolbarButton';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: ToolbarButton,
title: 'Components/ToolbarButton',
};
<Meta title="Components/ToolbarButton" component={ToolbarButton} />
export const ToolbarButtonTemplate = (args) => (
<div className="bg-primary-dark h-16">
<div class="w-8">
<ToolbarButton {...args} />
</div>
</div>
);
<Heading
title="ToolbarButton"
componentRelativePath="ToolbarButton/ToolbarButton.js"
/>
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
ToolbarButton is a component that renders the ToolbarButtons.
<Canvas>
<Story
name="Overview"
args={{
type: 'tool',
id: 'Zoom',
icon: 'tool-zoom',
label: 'Zoom',
commandOptions: {
toolName: 'Zoom',
},
dropdownContent: null,
isActive: false,
bState: {
primaryToolId: 'Wwwc',
toggles: {},
groups: {
primary: 'Wwwc',
},
},
}}
>
{ToolbarButtonTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={ToolbarButton} />
## Contribute
<Footer componentRelativePath="ToolbarButton/__stories__/toolbarButton.stories.mdx" />

View File

@ -0,0 +1,79 @@
import Tooltip from '../Tooltip';
import Icon from '../../Icon';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Tooltip,
title: 'Components/Tooltip',
};
<Meta title="Components/Tooltip" component={Tooltip} />
export const TooltipTemplate = (args) => (
<div className="h-16 w-full">
<div class="w-8 h-8 mx-auto">
<Tooltip {...args}>
<Icon name="clipboard" />
</Tooltip>
</div>
</div>
);
<Heading title="Tooltip" componentRelativePath="Tooltip/Tooltip.js" />
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
Tooltip is a component that renders the Tooltips.
<Canvas>
<Story
name="Overview"
args={{
content: 'Tooltip',
}}
>
{TooltipTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Tooltip} />
## Usage
### Position
You can set the position of the tooltip by using the `position` prop.
<Canvas>
<Story name="Position">
<div className="h-16 w-full m-8">
<div class="w-8 h-8 mx-auto">
<Tooltip position="right" content="right">
<Icon name="clipboard" />
<span>Right</span>
</Tooltip>
</div>
</div>
<div className="h-16 w-full m-8">
<div class="w-8 h-8 mx-auto">
<Tooltip position="left" content="left">
<Icon name="clipboard" />
<span>Left</span>
</Tooltip>
</div>
</div>
</Story>
</Canvas>
## Contribute
<Footer componentRelativePath="Tooltip/__stories__/tooltip.stories.mdx" />

View File

@ -0,0 +1,152 @@
import Typography from '../Typography';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
export const argTypes = {
component: Typography,
title: 'Foundations/Typography',
};
<Meta title="Foundations/Typography" component={Typography} />
<Heading title="Typography" componentRelativePath="Typography/Typography.js" />
- [Overview](#overview)
- [Props](#props)
- [Usage](#usage)
- [Contribute](#contribute)
## Overview
Typography is a component that renders the Typographys.
<Canvas>
<Story name="Overview">
<Typography variant="h1" className="text-blue-800">
Heading 1
</Typography>
</Story>
</Canvas>
### Usage
#### Variants
There are several variants of the Typography component.
<Canvas>
<Story name="Variants">
<Typography variant="h1" className="text-blue-800">
Heading 1
</Typography>
<Typography variant="h2" className="text-blue-800">
Heading 2
</Typography>
<Typography variant="h3" className="text-blue-800">
Heading 3
</Typography>
<Typography variant="h4" className="text-blue-800">
Heading 4
</Typography>
<Typography variant="h5" className="text-blue-800">
Heading 5
</Typography>
<Typography variant="h6" className="text-blue-800">
Heading 6
</Typography>
<Typography variant="p" className="text-blue-800">
Paragraph
</Typography>
<Typography variant="subtitle" className="text-blue-800">
Subtitle
</Typography>
<Typography variant="caption" className="text-blue-800">
Caption
</Typography>
<Typography variant="body" className="text-blue-800">
Body
</Typography>
</Story>
</Canvas>
#### Text Color
You can use the `color` prop to change the color of the text.
<Canvas>
<Story name="Text Color">
<Typography variant="h5" color="primary">
Primary Color
</Typography>
<Typography variant="h5" color="secondary">
Secondary Color
</Typography>
<Typography variant="h5" color="error">
Error Color
</Typography>
<Typography variant="h5" color="inherit">
Inherit Color
</Typography>
</Story>
</Canvas>
#### Custom className
You can use the `className` prop to add a custom className to the Typography.
<Canvas>
<Story name="Custom Class">
<Typography variant="h5" className="text-red-400">
Custom Red Color
</Typography>
</Story>
</Canvas>
#### Align
<Canvas>
<Story name="Align">
<Typography variant="body" align="left" color="primary">
Align Left: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
tincidunt tempus ante, non blandit sem vehicula ac. Vestibulum in dolor
commodo, elementum massa scelerisque, molestie nulla. Aenean sed odio
interdum, rutrum ex at, volutpat diam. Donec vel tristique sem, eget
iaculis velit. Aenean placerat justo
</Typography>
<br />
<Typography variant="body" align="center" color="primary">
Align Center: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
tincidunt tempus ante, non blandit sem vehicula ac. Vestibulum in dolor
commodo, elementum massa scelerisque, molestie nulla. Aenean sed odio
interdum, rutrum ex at, volutpat diam. Donec vel tristique sem, eget
iaculis velit. Aenean placerat justo
</Typography>
<br />
<Typography variant="body" align="right" color="primary">
Align Right: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
tincidunt tempus ante, non blandit sem vehicula ac. Vestibulum in dolor
commodo, elementum massa scelerisque, molestie nulla. Aenean sed odio
interdum, rutrum ex at, volutpat diam. Donec vel tristique sem, eget
iaculis velit. Aenean placerat justo
</Typography>
<br />
<Typography variant="body" align="justify" color="primary">
Align Justify: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed tincidunt tempus ante, non blandit sem vehicula ac. Vestibulum in
dolor commodo, elementum massa scelerisque, molestie nulla. Aenean sed
odio interdum, rutrum ex at, volutpat diam. Donec vel tristique sem, eget
iaculis velit. Aenean placerat justo
</Typography>
</Story>
</Canvas>
## Props
<ArgsTable of={Typography} />
## Usage
## Contribute
<Footer componentRelativePath="Typography/__stories__/typography.stories.mdx" />

View File

@ -0,0 +1,67 @@
import Viewport from "../Viewport";
import { ArgsTable, Story, Canvas, Meta } from "@storybook/addon-docs";
import { createComponentTemplate } from "../../../storybook/functions/create-component-story";
export const argTypes = {
component: Viewport,
title: "Components/Viewport",
};
<Meta title="Components/Viewport" component={Viewport} />
export const ViewportTemplate = (args) => (
<div className="h-40 bg-primary-dark">
<Viewport {...args} />
</div>
);
<Heading title="Viewport" componentRelativePath="Viewport/Viewport.jsx" />
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
Viewport is a component that renders the Viewport action bar.
<Canvas>
<Story
name="Overview"
args={{
viewportIndex: 0,
onSeriesChange: (direction) => alert(`Annotation ${direction}`),
studyData: {
label: "A",
isTracked: true,
isLocked: false,
isRehydratable: false,
studyDate: "07-Sep-2011",
currentSeries: 1,
seriesDescription:
"Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ",
modality: "CT",
patientInformation: {
patientName: "Smith, Jane",
patientSex: "F",
patientAge: "59",
MRN: "10000001",
thickness: "5.0mm",
spacing: "1.25mm",
scanner: "Aquilion",
},
},
}}
>
{ViewportTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={Viewport} />
## Contribute
<Footer componentRelativePath="Viewport/__stories__/Viewport.stories.mdx" />

View File

@ -239,11 +239,11 @@ const ViewportActionBar = ({
};
const borderColor = useAltStyling ? '#365A6A' : '#1D205A';
const backgroundColor = useAltStyling
? '#031923'
: isTracked
? '#020424'
: null;
let backgroundColor = '#020424';
if (useAltStyling || isTracked) {
backgroundColor = '#031923'
}
return (
<div

View File

@ -0,0 +1,35 @@
import React from 'react'
import ViewportActionBar from './ViewportActionBar'
export default {
component: ViewportActionBar,
title: 'Components/ViewportActionBar',
}
export const Default = () => (
<div className="w-1/2 p-4 h-64">
<ViewportActionBar
onSeriesChange={direction => alert(`Series ${direction}`)}
studyData={{
label: 'A',
isTracked: false,
isLocked: false,
isRehydratable: true,
studyDate: '07-Sep-2010',
currentSeries: 1,
seriesDescription:
'Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ',
modality: 'CT',
patientInformation: {
patientName: 'Smith, Jane',
patientSex: 'F',
patientAge: '59',
MRN: '10000001',
thickness: '2.0mm',
spacing: '1.25mm',
scanner: 'Aquilion',
},
}}
/>
</div>
)

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

View File

@ -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 }} />;
};

View File

@ -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>

View File

@ -0,0 +1,3 @@
export const AnchorListItem = ({ children }) => {
return <li className="text-gray-800">{children}</li>;
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);

View File

@ -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,
};

View File

@ -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>
);
};

View File

@ -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,
};

View File

@ -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>
```

View File

@ -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;
}

View File

@ -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>
);

View File

@ -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" />

View File

@ -5,6 +5,8 @@
@tailwind components;
@tailwind utilities;
/* Truncate 2 lines utility */
.truncate-2-lines {
overflow: hidden;
@ -14,3 +16,4 @@
line-height: 1.3;
height: 2.1rem;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -3,7 +3,7 @@ module.exports = {
important: false,
separator: ':',
purge: {
content: ['./src/**/*.{jsx,js,css}'],
content: ['./src/**/*.{jsx,js,ts,tsx,css,mdx}'],
safelist: [],
},
theme: {
@ -13,6 +13,9 @@ module.exports = {
lg: '1024px',
xl: '1280px',
},
fontFamily: {
inter: ['Inter', 'sans-serif'],
},
colors: {
overlay: 'rgba(0, 0, 0, 0.8)',
transparent: 'transparent',

View File

@ -48,7 +48,7 @@
"cornerstone-core": "^2.6.0"
},
"dependencies": {
"@babel/runtime": "7.7.6",
"@babel/runtime": "7.16.3",
"@ohif/core": "^2.5.1",
"@ohif/extension-cornerstone": "^2.4.0",
"@ohif/extension-measurement-tracking": "^0.0.1",

9054
yarn.lock

File diff suppressed because it is too large Load Diff