diff options
Diffstat (limited to 'stories/react')
24 files changed, 1224 insertions, 0 deletions
diff --git a/stories/react/Accordion.stories.js b/stories/react/Accordion.stories.js new file mode 100644 index 0000000..85fdae3 --- /dev/null +++ b/stories/react/Accordion.stories.js @@ -0,0 +1,16 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Accordion from '../../src/react/Accordion.js'; +import HTMLBasic from '../../components/accordion/accordion-basic.html'; +let examples = { + Basic: { + jsx: <Accordion title='Accordion Title'><div>Accordion body</div></Accordion>, + html: HTMLBasic + } +}; + +const Checkboxes = () => ( + <Examples examples={examples} /> +); + +export default Checkboxes; diff --git a/stories/react/Checkbox.stories.js b/stories/react/Checkbox.stories.js new file mode 100644 index 0000000..3fb3ad1 --- /dev/null +++ b/stories/react/Checkbox.stories.js @@ -0,0 +1,33 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; + +import Checkbox from '../../src/react/Checkbox'; +import HTMLCheckboxChecked from '../../components/checkbox/checkbox-checked.html'; +import HTMLCheckboxUnchecked from '../../components/checkbox/checkbox-unchecked.html'; +import HTMLCheckboxDisabled from '../../components/checkbox/checkbox-disabled.html'; +import HTMLCheckboxDisabledChecked from '../../components/checkbox/checkbox-disabled-checked.html'; + +let examples = { + Checked: { + jsx: <Checkbox checked={true} label='This is the checkbox label' value='myVal' onChange={()=>{}} data-test-id='mycheckbox-1' inputRef={() => {} } />, + html: HTMLCheckboxChecked + }, + Unchecked: { + jsx: <Checkbox label='This is the checkbox label' value='myVal' onChange={()=>{}} data-test-id='mycheckbox-2' inputRef={() => {} } />, + html: HTMLCheckboxUnchecked + }, + Disabled: { + jsx: <Checkbox label='This is the checkbox label' disabled={true} value='myVal' onChange={()=>{}} data-test-id='mycheckbox-4' inputRef={() => {} } />, + html: HTMLCheckboxDisabled + }, + 'Disabled and Checked': { + jsx: <Checkbox label='This is the checkbox label' disabled={true} checked={true} value='myVal' onChange={()=>{}} data-test-id='mycheckbox-4' inputRef={() => {} } />, + html: HTMLCheckboxDisabledChecked + } +}; + +const Checkboxes = () => ( + <Examples examples={examples} /> +); + +export default Checkboxes; diff --git a/stories/react/Checklist.stories.js b/stories/react/Checklist.stories.js new file mode 100644 index 0000000..0fd089b --- /dev/null +++ b/stories/react/Checklist.stories.js @@ -0,0 +1,65 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Checklist from '../../src/react/Checklist.js'; +import HTMLListChecked from '../../components/checklist/checklist-with-checked-items.html'; +import HTMLListDisabled from '../../components/checklist/checklist-with-disabled-items.html'; +const items = [ + { + label: 'apple', + value: 'apple', + dataTestId: 'apple', + checked: true + }, + { + label: 'banana', + value: 'banana', + dataTestId: 'banana', + checked: false + }, + { + label: 'orange', + value: 'orange', + dataTestId: 'orange', + checked: true + } +]; + +const itemsDisabled = [ + { + label: 'apple', + value: 'apple', + dataTestId: 'apple', + checked: true, + disabled: true + }, + { + label: 'banana', + value: 'banana', + dataTestId: 'banana', + checked: false, + disabled: true + }, + { + label: 'orange', + value: 'orange', + dataTestId: 'orange', + checked: false + } +]; + +let examples = { + Basic: { + jsx: <Checklist items={items} onChange={() => { }} />, + html: HTMLListChecked + }, + Disabled: { + jsx: <Checklist items={itemsDisabled} onChange={() => { }} />, + html: HTMLListDisabled + } +}; + +const ChecklistStory = () => ( + <Examples examples={examples} /> +); + +export default ChecklistStory;
\ No newline at end of file diff --git a/stories/react/Colors.stories.js b/stories/react/Colors.stories.js new file mode 100644 index 0000000..d6758ce --- /dev/null +++ b/stories/react/Colors.stories.js @@ -0,0 +1,53 @@ +import React, {Component} from 'react'; + +const colorMap = { + '$white': '#ffffff', + '$blue': '#009fdb', + '$light-blue': '#1eb9f3', + '$lighter-blue': '#e6f6fb', + '$blue-disabled': '#9dd9ef', + '$dark-blue': '#0568ae', + '$black': '#000000', + '$rich-black': '#323943', + '$text-black': '#191919', + '$dark-gray': '#5a5a5a', + '$gray': '#959595', + '$light-gray': '#d2d2d2', + '$silver': '#eaeaea', + '$light-silver': '#f2f2f2', + '$green': '#4ca90c', + '$functional-red': '#cf2a2a', + '$yellow': '#ffb81c', + '$dark-purple': '#702f8a', + '$purple': '#9063cd', + '$light-purple': '#caa2dd' +}; + +function Color({colorName, colorValue}) { + return ( + <div key={colorName} className='color-section'> + <div className='color-circle' style={{backgroundColor: colorValue}} /> + <div>{colorName.replace('$', '')}</div> + <div>{colorValue}</div> + </div> + ); +} + +class Colors extends Component { + + render() { + return ( + <div> + <h1>Colors Palette</h1> + <div className='colors-table'> + { + Object.keys(colorMap).map(colorName => <Color key={colorName} colorValue={colorMap[colorName]} colorName={colorName}/>) + } + </div> + </div> + ); + } + +} + +export default Colors; diff --git a/stories/react/Input.stories.js b/stories/react/Input.stories.js new file mode 100644 index 0000000..869bafa --- /dev/null +++ b/stories/react/Input.stories.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import Examples from './utils/Examples.js'; + +import ReactInput from '../../src/react/Input.js'; + +import InputDefaultHtml from '../../components/input/input.html'; +import InputRequiredHtml from '../../components/input/input-required.html'; +import InputNumberHtml from '../../components/input/input-number.html'; +import InputViewOnlyHtml from '../../components/input/input-view-only.html'; +import InputDisabledHtml from '../../components/input/input-disabled.html'; +import InputPlaceholderHtml from '../../components/input/input-placeholder.html'; +import InputErrorHtml from '../../components/input/input-error.html'; + +let examples = { + 'Input Default': { + jsx: <ReactInput name='input1' value='Default' label='I am a label' onChange={ action('input-change')}/>, + html: InputDefaultHtml + }, + 'Input Required': { + jsx: <ReactInput name='input2' value='Default' label='I am a label' onChange={ action('input-change')} isRequired/>, + html: InputRequiredHtml + }, + 'Input Number': { + jsx: <ReactInput name='input3' value='3' label='I am a label' type="number" onChange={ action('input-change')}/>, + html: InputNumberHtml + }, + 'Input View Only': { + jsx: <ReactInput value='Read Only Text' label='I am a label' onChange={ action('input-change')} readOnly/>, + html: InputViewOnlyHtml + }, + 'Input Disabled': { + jsx: <ReactInput value='Default' label='I am a label' onChange={ action('input-change')} disabled/>, + html: InputDisabledHtml + }, + 'Input Placeholder': { + jsx: <ReactInput name='input5' placeholder='Write Here...' label='I am a label' onChange={ action('input-change')}/>, + html: InputPlaceholderHtml + }, + 'Input Error': { + jsx: <ReactInput value='Default' name='input6' label='I am a label' errorMessage='This is the error message' onChange={ action('input-change')}/>, + html: InputErrorHtml + } + +} + +const Inputs = () => ( + <Examples examples={examples} /> +); + +export default Inputs; diff --git a/stories/react/Modal.stories.js b/stories/react/Modal.stories.js new file mode 100644 index 0000000..29ff7a5 --- /dev/null +++ b/stories/react/Modal.stories.js @@ -0,0 +1,133 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Button from '../../src/react/Button.js'; +import Modal from '../../src/react/Modal.js'; +import Input from '../../src/react/Input.js'; +import HTMLStandardModal from '../../components/modal/standard-modal.html'; +import HTMLAlertModal from '../../components/modal/alert-modal.html'; +import HTMLErrorModal from '../../components/modal/error-modal.html'; +import HTMLCustomModal from '../../components/modal/custom-modal.html'; + +class Example extends React.Component { + constructor(props) { + super(props); + this.state = { + show: false + }; + } + render() { + const { children } = this.props; + const { show } = this.state; + var childrenWithProps = React.Children.map(children, child => { + let childChildrenWithProps = []; + if (child.props.children) { + let childChildren = child.props.children; + childChildrenWithProps = React.Children.map(childChildren, child => + React.cloneElement(child, { onClose: ()=>this.setState({show: !show}) })); + + } + return React.cloneElement(child, { show: this.state.show, onClose: ()=>this.setState({show: !show}), children: childChildrenWithProps}); + } + ); + + return ( + <div> + <Button onClick={() => this.setState({show: !show})}>Modal</Button> + {childrenWithProps} + </div> + ); + } +} + +const ModalBody = () => { + return ( + <div> + <Input + name='input1' + value='Default' + label='I am a label' + type='text' /> + <Input + name='input1' + value='Default' + label='I am a label' + type='text' /> + <Input + name='input1' + value='Default' + label='I am a label' + type='text' /> + + </div>); +}; + +const BODY_TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,' + + 'pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra'; + +const isShown = false; + +let examples = { + Standard: { + jsx: <Example> + <Modal show={() => isShown()} size='small'> + <Modal.Header><Modal.Title>Standard Modal</Modal.Title></Modal.Header> + <Modal.Body> + {BODY_TEXT} + </Modal.Body> + <Modal.Footer actionButtonText='Yes' actionButtonClick={()=>{}}/> + </Modal> + </Example>, + html: HTMLStandardModal, + exclude: 'Example', + renderFromJsx: true + }, + Alert: { + jsx: <Example> + <Modal show={() => isShown()} type='alert' size='small'> + <Modal.Header type='alert'><Modal.Title>Title</Modal.Title></Modal.Header> + <Modal.Body> + {BODY_TEXT} + </Modal.Body> + <Modal.Footer closeButtonText='Ok'/> + </Modal> + </Example>, + html: HTMLAlertModal, + exclude: 'Example', + renderFromJsx: true + }, + Error: { + jsx: <Example> + <Modal show={() => isShown()} size='small' type='error'> + <Modal.Header onClose={()=>isShown(false)} type='error'><Modal.Title>Title</Modal.Title></Modal.Header> + <Modal.Body> + {BODY_TEXT} + </Modal.Body> + <Modal.Footer onClose={()=>isShown(false)} closeButtonText='Ok'/> + </Modal> + </Example>, + html: HTMLErrorModal, + exclude: 'Example', + renderFromJsx: true + }, + + Custom: { + jsx: <Example> + <Modal show={() => isShown()} type='custom'> + <Modal.Header type='custom'><Modal.Title>Title</Modal.Title></Modal.Header> + <Modal.Body> + <ModalBody/> + </Modal.Body> + <Modal.Footer actionButtonText='Ok' actionButtonClick={()=>{}}/> + </Modal> + </Example>, + html: HTMLCustomModal, + exclude: 'Example', + renderFromJsx: true + } +}; + +const Modals = () => ( + <Examples examples={examples}/> +); + +export default Modals;
\ No newline at end of file diff --git a/stories/react/Panel.stories.js b/stories/react/Panel.stories.js new file mode 100644 index 0000000..f87eefb --- /dev/null +++ b/stories/react/Panel.stories.js @@ -0,0 +1,22 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Panel from '../../src/react/Panel.js'; +import Checkbox from '../../src/react/Checkbox.js'; +import HTMLBasic from '../../components/panel/basic-panel.html'; +let examples = { + Basic: { + jsx: + <Panel> + <h3>Panel</h3> + <Checkbox label='filter-item' /> + <Checkbox checked label='filter-item-checked' /> + </Panel>, + html: HTMLBasic + } +}; + +const PanelStory = () => ( + <Examples examples={examples} /> +); + +export default PanelStory; diff --git a/stories/react/PopupMenu.stories.js b/stories/react/PopupMenu.stories.js new file mode 100644 index 0000000..9d94522 --- /dev/null +++ b/stories/react/PopupMenu.stories.js @@ -0,0 +1,37 @@ + +import React from 'react'; +import Examples from './utils/Examples.js'; +import PopupMenu from '../../src/react/PopupMenu.js'; +import PopupMenuItem from '../../src/react/PopupMenuItem.js'; +import HTMLPopupMenu from '../../components/menu/popup-menu.html'; +import HTMLPopupMenuRelative from '../../components/menu/relative-popup-menu.html'; + +let examples = { + 'Basic popup menu (static)': { + jsx: <PopupMenu onMenuItemClick={() => {}}> + <PopupMenuItem itemId='1' value='item 1 (selected)' selected/> + <PopupMenuItem itemId='2' value='item 2' disabled/> + <PopupMenu.Separator /> + <PopupMenuItem itemId='3' value='item 3'/> + <PopupMenuItem itemId='4' value='custom action' onClick={function customCallback() {}}/> + </PopupMenu>, + html: HTMLPopupMenu + }, + 'Basic popup menu (relative)': { + jsx: <div className='sdc-popup-menu'> + <PopupMenu onMenuItemClick={()=> {}} position={{x: 10, y: 10}} relative> + <PopupMenuItem itemId='1' value='item 1 (selected)' selected/> + <PopupMenuItem itemId='2' value='item 2' disabled/> + <PopupMenu.Separator /> + <PopupMenuItem itemId='3' value='item 3' onClick={function customCallback() {}}/> + </PopupMenu> + </div>, + html: HTMLPopupMenuRelative + } +}; + +const PopupMenuReactComponent = () => ( + <Examples examples={examples} /> +); + +export default PopupMenuReactComponent;
\ No newline at end of file diff --git a/stories/react/Radio.stories.js b/stories/react/Radio.stories.js new file mode 100644 index 0000000..151f947 --- /dev/null +++ b/stories/react/Radio.stories.js @@ -0,0 +1,33 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; + +import Radio from '../../src/react/Radio'; +import HTMLRadioChecked from '../../components/radio/radio-checked.html'; +import HTMLRadioUnchecked from '../../components/radio/radio-unchecked.html'; +import HTMLRadioDisabled from '../../components/radio/radio-disabled.html'; +import HTMLRadioDisabledChecked from '../../components/radio/radio-disabled-checked.html'; + +let examples = { + Checked: { + jsx: <Radio name='grp1' checked={true} label='This is the radio label' value='myVal' onChange={()=>{}} data-test-id='myradio-1' inputRef={() => {} } />, + html: HTMLRadioChecked + }, + Unchecked: { + jsx: <Radio name='grp2' label='This is the radio label' value='myVal' onChange={()=>{}} data-test-id='myradio-2' inputRef={() => {} } />, + html: HTMLRadioUnchecked + }, + Disabled: { + jsx: <Radio name='grp3' label='This is the radio label' disabled={true} value='myVal' onChange={()=>{}} data-test-id='myradio-4' inputRef={() => {} } />, + html: HTMLRadioDisabled + }, + 'Disabled and Checked': { + jsx: <Radio name='grp4' label='This is the radio label' disabled={true} checked={true} value='myVal' onChange={()=>{}} data-test-id='myradio-4' inputRef={() => {} } />, + html: HTMLRadioDisabledChecked + } +}; + +const Radios = () => ( + <Examples examples={examples} /> +); + +export default Radios; diff --git a/stories/react/RadioGroup.stories.js b/stories/react/RadioGroup.stories.js new file mode 100644 index 0000000..912f9b9 --- /dev/null +++ b/stories/react/RadioGroup.stories.js @@ -0,0 +1,34 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; + +import RadioGroup from '../../src/react/RadioGroup'; +import HTMLRadioGroup from '../../components/radioGroup/radio-group.html'; +import HTMLRadioGroupValue from '../../components/radioGroup/radio-group-value.html'; +import HTMLRadioGroupDisabled from '../../components/radioGroup/radio-group-disabled.html'; +import HTMLRadioGroupNoTitle from '../../components/radioGroup/radio-group-no-title.html'; + +let examples = { + 'Value': { + jsx: <RadioGroup name='grp2' value='1' title='Group B' onChange={()=>{}} data-test-id='grp2' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroupValue + }, + 'No Value': { + jsx: <RadioGroup name='grp3' title='Group C' onChange={()=>{}} data-test-id='grp3' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroup + }, + 'Disabled': { + jsx: <RadioGroup name='grp4' disabled={true} title='Group D' onChange={()=>{}} data-test-id='grp4' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroupDisabled + }, + 'No title': { + jsx: <RadioGroup name='grp5' onChange={()=>{}} data-test-id='grp4' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroupNoTitle + } + +}; + +const RadioGroups = () => ( + <Examples examples={examples} /> +); + +export default RadioGroups; diff --git a/stories/react/SVGIcon.stories.js b/stories/react/SVGIcon.stories.js new file mode 100644 index 0000000..2c2ffc2 --- /dev/null +++ b/stories/react/SVGIcon.stories.js @@ -0,0 +1,103 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import DropdownMenu from './utils/components/DropdownMenu.js'; +import SVGIcon from '../../src/react/SVGIcon.js'; + +const iconLabelPositions = [ + '', 'bottom', 'top', 'left', 'right' +]; + +const iconColors = [ + '', + 'primary', + 'secondary', + 'positive', + 'negative', + 'warning' +]; + +const disabledStates = ['false', 'true']; + +function buildExamples({iconName, iconLabel, labelPosition, color, disabled}) { + return { + Example: { + jsx: <SVGIcon + label={iconLabel} + labelPosition={labelPosition} + color={color} + name={iconName} + disabled={disabled === 'true'} /> + } + }; +} + +const IconTable = ({onClick}) => ( + <div className='icons-table'> + {ICON_NAMES.map(icon => ( + <div key={icon} className='icon-section'> + <SVGIcon + onClick={() => onClick(icon)} + label={icon} + iconClassName='storybook-small' + name={icon} /> + </div> + ))} + </div> +); + +class Icons extends React.Component { + constructor(props) { + super(props); + this.state = { + iconName: ICON_NAMES[0], + iconLabel: '', + labelPosition: iconLabelPositions[0], + color : iconColors[0] + }; + } + + render() { + let {iconName, iconLabel, labelPosition, color, disabled} = this.state; + return ( + <div className='icons-screen'> + <h1>Icons</h1> + <div className='icons-option-selector'> + <DropdownMenu + title='Icon name' + value={iconName} + onChange={e => this.setState({iconName: e.target.value})} + options={ICON_NAMES} /> + <div className='option-container'> + <label>Icon label</label> + <input value={iconLabel} onChange={e => this.setState({iconLabel: e.target.value})}/> + </div> + <DropdownMenu + title='Label position' + value={labelPosition} + onChange={e => this.setState({labelPosition: e.target.value})} + options={iconLabelPositions} /> + <DropdownMenu + title='Color' + value={color} + onChange={e => this.setState({color: e.target.value})} + options={iconColors} /> + <DropdownMenu + title='Disabled' + value={disabled} + onChange={e => this.setState({disabled: e.target.value})} + options={disabledStates} /> + </div> + <Examples examples={buildExamples({iconName, iconLabel, labelPosition, color, disabled})} /> + <IconTable onClick={icon => this.setState({iconName: icon})} /> + <div className='missing-icon-section'> + <div >You will see the following if the icon name you used is not found:</div> + <SVGIcon + onClick={() => {}} + name='MissingIcon' /> + </div> + </div> + ); + }; +} + +export default Icons; diff --git a/stories/react/Tabs.stories.js b/stories/react/Tabs.stories.js new file mode 100644 index 0000000..74f163c --- /dev/null +++ b/stories/react/Tabs.stories.js @@ -0,0 +1,48 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import {default as TabsComp} from '../../src/react/Tabs.js'; +import Tab from '../../src/react/Tab.js'; +import HTMLTabsHeader from '../../components/tabs/tabs-header.html'; +import HTMLTabsDisabled from '../../components/tabs/tabs-disabled.html'; +import HTMLTabsMenu from '../../components/tabs/tabs-menu.html'; + +let examples = { + 'Menu Tabs': { + jsx: <TabsComp type='menu' activeTab='1' onTabClick={(tabId) => {console.log(tabId);}}> + <Tab title='tab 1' tabId='1'> + <div>This is the active tab content</div> + </Tab> + <Tab title='tab 2' tabId='2' /> + <Tab title='tab 3' tabId='3' /> + </TabsComp>, + html: HTMLTabsMenu + }, + 'Header Tabs': { + jsx: <TabsComp type='header' activeTab='1' onTabClick={(tabId) => {console.log(tabId);}}> + <Tab title='tab 1' tabId='1'> + <div>This is the active tab content</div> + </Tab> + <Tab title='tab 2' tabId='2' /> + <Tab title='tab 3' tabId='3' /> + </TabsComp>, + html: HTMLTabsHeader + }, + 'Disabled Tabs': { + jsx: ( + <TabsComp type='header' activeTab='1' onTabClick={(tabId) => {console.log(tabId);}}> + <Tab title='tab 1' tabId='1'> + <div>This is the active tab content</div> + </Tab> + <Tab title='tab 2' tabId='2' disabled/> + <Tab title='tab 3' tabId='3' disabled/> + </TabsComp> + ), + html: HTMLTabsDisabled + } +}; + +const Tabs = () => ( + <Examples examples={examples} /> +); + +export default Tabs; diff --git a/stories/react/Tiles.stories.js b/stories/react/Tiles.stories.js new file mode 100644 index 0000000..04a6fb5 --- /dev/null +++ b/stories/react/Tiles.stories.js @@ -0,0 +1,89 @@ +import React from 'react'; + +import Examples from './utils/Examples.js'; +import SVGIcon from '../../src/react/SVGIcon.js'; +import Button from '../../src/react/Button.js'; + +import Tile from '../../src/react/Tile.js'; +import TileInfo from '../../src/react/TileInfo.js'; +import TileInfoLine from '../../src/react/TileInfoLine.js'; +import TileFooter from '../../src/react/TileFooter.js'; +import TileFooterCell from '../../src/react/TileFooterCell.js'; + +import HTMLTileWithoutFooter from '../../components/tile/tile-without-footer.html'; +import HTMLVspTile from '../../components/tile/vsp-tile.html'; +import HTMLVlmTile from '../../components/tile/vlm-tile.html'; +import HTMLVendorTile from '../../components/tile/vendor-tile.html'; +import HTMLVfcTile from '../../components/tile/vfc-tile.html'; + +let examples = { + 'Without footer': { + jsx: <Tile headerText='header' headerColor='blue' iconName='vsp' iconColor='blue'> + <TileInfo> + <TileInfoLine type='supertitle'>Supertitle</TileInfoLine> + <TileInfoLine type='title'>Title</TileInfoLine> + </TileInfo> + </Tile>, + html: HTMLTileWithoutFooter + }, + VFC: { + jsx: <Tile headerText='vfc' headerColor='purple' iconName='network'> + <TileInfo> + <TileInfoLine type='title'>Title</TileInfoLine> + <TileInfoLine type='subtitle'>V 1.0</TileInfoLine> + </TileInfo> + <TileFooter> + <TileFooterCell>Certified</TileFooterCell> + </TileFooter> + </Tile>, + html: HTMLVfcTile + }, + VSP: { + jsx: <Tile headerText='vsp' headerColor='blue' iconName='vsp' iconColor='blue'> + <TileInfo> + <TileInfoLine type='supertitle'>VLM</TileInfoLine> + <TileInfoLine type='title'>VSP name</TileInfoLine> + </TileInfo> + <TileFooter> + <TileFooterCell>Draft</TileFooterCell> + </TileFooter> + </Tile>, + html: HTMLVspTile + }, + VLM: { + jsx: <Tile headerText='vlm' headerColor='purple' iconName='vlm' iconColor='purple'> + <TileInfo> + <TileInfoLine type='title'>VLM name</TileInfoLine> + </TileInfo> + <TileFooter> + <TileFooterCell>Certified</TileFooterCell> + <TileFooterCell> + <SVGIcon name='versionControllerPermissions' label='Owner' labelPosition='left' /> + </TileFooterCell> + </TileFooter> + </Tile>, + html: HTMLVlmTile + }, + Vendor: { + jsx: <Tile iconName='vendor' iconColor='dark-gray'> + <TileInfo align='center'> + <TileInfoLine type='title'>Vendor name</TileInfoLine> + <TileInfoLine> + <Button btnType='primary' onClick={() => {}}>100 VSPs</Button> + </TileInfoLine> + </TileInfo> + <TileFooter align='center'> + <TileFooterCell> + <Button btnType='link' color='primary' iconName='plusThin' onClick={() => {}}>Create new VSP</Button> + </TileFooterCell> + </TileFooter> + </Tile>, + html: HTMLVendorTile + }, +}; + +const Tiles = () => ( + <Examples examples={examples} /> +); + +export default Tiles; diff --git a/stories/react/Typography.stories.js b/stories/react/Typography.stories.js new file mode 100644 index 0000000..f1475c6 --- /dev/null +++ b/stories/react/Typography.stories.js @@ -0,0 +1,62 @@ +import React, {Component} from 'react'; + +const typos = [ + {className: 'heading-1', size: 28, text: 'Major Section Heading'}, + {className: 'heading-2', size: 24, text: 'Sub-Section Heading'}, + {className: 'heading-3', size: 20, text: 'Small Heading'}, + {className: 'heading-4', size: 16, text: 'Small Heading'}, + {className: 'heading-4-emphasis', size: 16, text: 'Small Heading'}, + {className: 'heading-5', size: 14, text: 'Small Heading'}, + {className: 'body-1', size: 14, text: 'Body (Standard) Text'}, + {className: 'body-1-italic', size: 14, text: 'Body (Standard) Text'}, + {className: 'body-2', size: 13, text: 'Text in Tables'}, + {className: 'body-2-emphasis', size: 13, text: 'Text in Tables'}, + {className: 'body-3', size: 12, text: 'Input Labels, Table Titles'}, + {className: 'body-3-emphasis', size: 12, text: 'Even Smaller Text'}, + {className: 'body-4', size: 10, text: 'Even Much Smaller Text'} +]; + +const fontWeights = ['OpenSans Regular 400', 'OpenSans Semibold 600']; + +function TextRow({className, size, text}) { + return ( + <div className={`typo-section ${className}`}> + <div>{className}</div> + <div>{size}px</div> + <div className='sample-text'>{text}</div> + </div> + ); +} + +class Typography extends Component { + + render() { + return ( + <div className='typography-screen'> + <h1>Typography</h1> + <div className='typography-section'> + <h3>Font Family</h3> + <ul> + <li>OpenSans</li> + <li style={{'fontFamily': 'Arial'}}>Arial</li> + <li style={{'fontFamily':'sans-serif'}}>sans-serif</li> + </ul> + </div> + <div className='typography-section'> + <h3>Font Weights</h3> + <ul>{fontWeights.map(font => <li key={font} className={font}>{font}</li>)}</ul> + </div> + <div className='typography-section'> + <h3>Font Size</h3> + <div className='typo-table'> + <TextRow className='SCSS mixin name (@include ....)' size='Size (in Pixels)' text='Sample Text'/> + {typos.map(typo => <TextRow key={typo.className} {...typo}/>)} + </div> + </div> + </div> + ); + } + +} + +export default Typography; diff --git a/stories/react/buttons/LinkButtons.stories.js b/stories/react/buttons/LinkButtons.stories.js new file mode 100644 index 0000000..ef32a22 --- /dev/null +++ b/stories/react/buttons/LinkButtons.stories.js @@ -0,0 +1,49 @@ +import React from 'react'; +import Examples from '../utils/Examples.js'; + +import ReactButton from '../../../src/react/Button.js'; + +import LinkButton from '../../../components/button/button-link.html'; +import LinkButtonDisabled from '../../../components/button/button-link-disabled.html'; +import ExtraSmall from '../../../components/button/button-link-extra-small.html'; +import Small from '../../../components/button/button-link-small.html'; +import Medium from '../../../components/button/button-link-medium.html'; +import Large from '../../../components/button/button-link-large.html'; +import Auto from '../../../components/button/button-link-auto.html'; + +let examples = { + 'Link Default': { + jsx: <ReactButton btnType='link' onClick={() => {}}>Click Me</ReactButton>, + html: LinkButton + }, + 'Link Disabled': { + jsx: <ReactButton btnType='link' onClick={() => {}} disabled>Click Me</ReactButton>, + html: LinkButtonDisabled, + }, + 'Extra Small': { + jsx: <ReactButton btnType='link' size='x-small' onClick={() => {}}>Click Me</ReactButton>, + html: ExtraSmall + }, + 'Small': { + jsx: <ReactButton btnType='link' size='small' onClick={() => {}}>Click Me</ReactButton>, + html: Small, + }, + 'Medium': { + jsx: <ReactButton btnType='link' size='medium' onClick={() => {}}>Click Me</ReactButton>, + html: Medium + }, + 'Large': { + jsx: <ReactButton btnType='link' size='large' onClick={() => {}}>Click Me</ReactButton>, + html: Large, + }, + 'Auto Sizing': { + jsx: <ReactButton btnType='link' size='default' onClick={() => {}}>Click Me</ReactButton>, + html: Auto, + } +}; + +const DefaultButtons = () => ( + <Examples examples={examples} /> +); + +export default DefaultButtons; diff --git a/stories/react/buttons/PrimaryButtons.stories.js b/stories/react/buttons/PrimaryButtons.stories.js new file mode 100644 index 0000000..db732b9 --- /dev/null +++ b/stories/react/buttons/PrimaryButtons.stories.js @@ -0,0 +1,49 @@ +import React from 'react'; +import Examples from '../utils/Examples.js'; + +import ReactButton from '../../../src/react/Button.js'; + +import PrimaryButton from '../../../components/button/button-primary.html'; +import PrimaryButtonDisabled from '../../../components/button/button-primary-disabled.html'; +import ExtraSmall from '../../../components/button/button-primary-extra-small.html'; +import Small from '../../../components/button/button-primary-small.html'; +import Medium from '../../../components/button/button-primary-medium.html'; +import Large from '../../../components/button/button-primary-large.html'; +import Auto from '../../../components/button/button-primary-auto.html'; + +let examples = { + 'Primary Default': { + jsx: <ReactButton onClick={() => {}}>Click Me</ReactButton>, + html: PrimaryButton + }, + 'Primary Disabled': { + jsx: <ReactButton onClick={() => {}} disabled>Click Me</ReactButton>, + html: PrimaryButtonDisabled, + }, + 'Extra Small': { + jsx: <ReactButton size='x-small' onClick={() => {}}>Click Me</ReactButton>, + html: ExtraSmall + }, + 'Small': { + jsx: <ReactButton size='small' onClick={() => {}}>Click Me</ReactButton>, + html: Small, + }, + 'Medium': { + jsx: <ReactButton size='medium' onClick={() => {}}>Click Me</ReactButton>, + html: Medium + }, + 'Large': { + jsx: <ReactButton size='large' onClick={() => {}}>Click Me</ReactButton>, + html: Large, + }, + 'Auto Sizing': { + jsx: <ReactButton size='default' onClick={() => {}}>Click Me</ReactButton>, + html: Auto, + } +}; + +const DefaultButtons = () => ( + <Examples examples={examples} /> +); + +export default DefaultButtons; diff --git a/stories/react/buttons/SecondaryButtons.stories.js b/stories/react/buttons/SecondaryButtons.stories.js new file mode 100644 index 0000000..75f9d54 --- /dev/null +++ b/stories/react/buttons/SecondaryButtons.stories.js @@ -0,0 +1,49 @@ +import React from 'react'; +import Examples from '../utils/Examples.js'; + +import ReactButton from '../../../src/react/Button.js'; + +import SecondaryButton from '../../../components/button/button-secondary.html'; +import SecondaryButtonDisabled from '../../../components/button/button-secondary-disabled.html'; +import ExtraSmall from '../../../components/button/button-secondary-extra-small.html'; +import Small from '../../../components/button/button-secondary-small.html'; +import Medium from '../../../components/button/button-secondary-medium.html'; +import Large from '../../../components/button/button-secondary-large.html'; +import Auto from '../../../components/button/button-secondary-auto.html'; + +let examples = { + 'Secondary Default': { + jsx: <ReactButton btnType='secondary' onClick={() => {}}>Click Me</ReactButton>, + html: SecondaryButton + }, + 'Secondary Disabled': { + jsx: <ReactButton btnType='secondary' onClick={() => {}} disabled>Click Me</ReactButton>, + html: SecondaryButtonDisabled, + }, + 'Extra Small': { + jsx: <ReactButton btnType='secondary' size='x-small' onClick={() => {}}>Click Me</ReactButton>, + html: ExtraSmall + }, + 'Small': { + jsx: <ReactButton btnType='secondary' size='small' onClick={() => {}}>Click Me</ReactButton>, + html: Small, + }, + 'Medium': { + jsx: <ReactButton btnType='secondary' size='medium' onClick={() => {}}>Click Me</ReactButton>, + html: Medium + }, + 'Large': { + jsx: <ReactButton btnType='secondary' size='large' onClick={() => {}}>Click Me</ReactButton>, + html: Large, + }, + 'Auto Sizing': { + jsx: <ReactButton btnType='secondary' size='default' onClick={() => {}}>Click Me</ReactButton>, + html: Auto, + } +}; + +const DefaultButtons = () => ( + <Examples examples={examples} /> +); + +export default DefaultButtons; diff --git a/stories/react/index.js b/stories/react/index.js new file mode 100644 index 0000000..6d425ba --- /dev/null +++ b/stories/react/index.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; + +import PrimaryButtons from './buttons/PrimaryButtons.stories.js'; +import SecondaryButtons from './buttons/SecondaryButtons.stories.js'; +import LinkButtons from './buttons/LinkButtons.stories.js'; + +import Colors from './Colors.stories.js'; +import Typography from './Typography.stories.js'; +import Checkboxes from './Checkbox.stories.js'; +import Checklist from './Checklist.stories.js'; +import Input from './Input.stories.js'; +import Icons from './SVGIcon.stories.js'; +import Tiles from './Tiles.stories.js'; +import Tabs from './Tabs.stories.js'; +import Radios from './Radio.stories.js'; +import RadioGroups from './RadioGroup.stories.js'; +import Modals from './Modal.stories.js'; +import PopupMenu from './PopupMenu.stories.js'; +import Accordion from './Accordion.stories.js'; +import Panel from './Panel.stories.js'; + +storiesOf('Colors', module) + .add('Color Palette', () => <Colors />); + +storiesOf('Typography', module) + .add('Typography', () => <Typography />); + +storiesOf('Accordion', module) + .add('Accordion', () => <Accordion />); + +storiesOf('Buttons', module) + .add('Primary', () => <PrimaryButtons />) + .add('Secondary', () => <SecondaryButtons />) + .add('Link', () => <LinkButtons />); + +storiesOf('Checkboxes', module) + .add('Checkboxes', () => <Checkboxes />); + +storiesOf('Checklist', module) + .add('Checklist', () => <Checklist />); + +storiesOf('Input Fields', module) + .add('Input Text', () => <Input />); + +storiesOf('Icons', module) + .add('SVG Icons', () => <Icons />); + +storiesOf('Menu', module) + .add('Popup Menu', () => <PopupMenu />); + +storiesOf('Modals', module) + .add('Modal examples', () => <Modals />); + +storiesOf('Radios', module) + .add('Radio Buttons', () => <Radios />) + .add('Radio Button Groups', () => <RadioGroups />); + +storiesOf('Panel', module) + .add('Panel', () => <Panel />); + +storiesOf('Tabs', module) + .add('Tabs', () => <Tabs />); + +storiesOf('Tiles', module) + .add('Tiles', () => <Tiles />); diff --git a/stories/react/utils/BeautifyHTML.js b/stories/react/utils/BeautifyHTML.js new file mode 100644 index 0000000..1a29b00 --- /dev/null +++ b/stories/react/utils/BeautifyHTML.js @@ -0,0 +1,33 @@ +export default function beautifyHTML({html, indentChar = ' ', startingIndentCount = 0}) { + html = html.replace(/[ ]{2,}/g, ' '); + + let result = '', indentCount = startingIndentCount, parsingText = false; + for (let i = 0; i < html.length; i++) { + + let startOfTag, endOfTag, closingTag, upcomingTag, afterTag, numTabs; + if (html[i] === '<') { startOfTag = true; } + else if (html[i] === '>') { endOfTag = true; } + else if (html[i - 1] === '>') { afterTag = true; } + if (html[i + 1] === '/') { closingTag = true; } + else if (html[i + 1 ] === '<') { upcomingTag = true; } + + if (startOfTag) { + if (closingTag) { numTabs = --indentCount; } + else { numTabs = indentCount++; } + } + + if (parsingText && afterTag) { + numTabs = indentCount; + } + + result += indentChar.repeat(numTabs) + html[i]; + + if (endOfTag || parsingText && upcomingTag) { + result += '\n'; + parsingText = false; + if (!upcomingTag) { parsingText = true; } + } + } + + return result.slice(0, -1); +} diff --git a/stories/react/utils/Examples.js b/stories/react/utils/Examples.js new file mode 100644 index 0000000..5948b68 --- /dev/null +++ b/stories/react/utils/Examples.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {renderToStaticMarkup} from 'react-dom/server'; +import SourceToggle from './SourceToggle.js'; +import beautifyHTML from './BeautifyHTML.js'; +import insertSVGIcons from './InsertSVGIcons.js'; + +const Examples = ({examples}) => ( + <div className={'examples'}> + {Object.keys(examples).map(key => { + let title = key; + let {jsx, html, displayTitle = true, exclude, renderFromJsx = false} = examples[key]; + if (!html) { + html = renderToStaticMarkup(jsx); + html = beautifyHTML({html, indentChar: ' '}); + } else { + html = insertSVGIcons({html, jsx}); + } + return <SourceToggle title={displayTitle && title} jsx={jsx} html={html} key={key} exclude={exclude} renderFromJsx={renderFromJsx}/>; + })} + </div> +); + +export default Examples; diff --git a/stories/react/utils/InsertSVGIcons.js b/stories/react/utils/InsertSVGIcons.js new file mode 100644 index 0000000..5a5e390 --- /dev/null +++ b/stories/react/utils/InsertSVGIcons.js @@ -0,0 +1,15 @@ +import {renderToStaticMarkup} from 'react-dom/server'; +import beautifyHTML from './BeautifyHTML.js'; + +const insertSVGIcons = ({html, jsx, indentChar = ' '}) => { + let svgCode = renderToStaticMarkup(jsx).match(/(<svg\b[^<>]*>)[\s\S]*?(<\/svg>)/g); + let newHTML = html.replace(/\s*<!-- insert SVG -->/g, str => { + let html = '\n' + svgCode.shift(); + let indentRegExp = new RegExp(`[${indentChar}]*`); + let startingIndentCount = str.slice(2).match(indentRegExp)[0].length / indentChar.length; + return beautifyHTML({html, startingIndentCount, indentChar}); + }); + return newHTML; +}; + +export default insertSVGIcons; diff --git a/stories/react/utils/SourceToggle.js b/stories/react/utils/SourceToggle.js new file mode 100644 index 0000000..a05c8d0 --- /dev/null +++ b/stories/react/utils/SourceToggle.js @@ -0,0 +1,73 @@ +/* eslint-disable react/no-danger */ +import React from 'react'; +import jsxToString from './jsxToString.js'; + +import Prism from 'prismjs'; + +import PrismJsx from 'prismjs/components/prism-jsx.js'; // eslint-disable-line no-unused-vars + +const sources = { + React: 'React', + HTML: 'HTML' +}; + +export default class SourceToggle extends React.Component { + constructor(props) { + super(props); + this.state = { + source: sources.React + }; + } + + renderFromSource() { + let {jsx, html, renderFromJsx} = this.props; + let {source} = this.state; + let classname = 'source-toggle-example'; + switch (source) { + case sources.HTML: + return renderFromJsx ? <div className={classname}>{jsx}</div> : <div className={classname} dangerouslySetInnerHTML={{__html: html}} />; + case sources.React: + default: + return <div className={classname}>{jsx}</div>; + } + } + + renderMarkdown() { + let {jsx, html, exclude} = this.props; + let {source} = this.state; + switch (source) { + case sources.HTML: + return {__html: Prism.highlight(html, Prism.languages.html)}; + case sources.React: + default: + return {__html: Prism.highlight(jsxToString({jsx, exclude}), Prism.languages.jsx)}; + } + } + + render() { + let {title} = this.props; + return ( + <div className='source-toggle-wrapper'> + {title && <div className='source-toggle-title'>{title}</div>} + <div className='source-toggle'> + {this.renderFromSource()} + <div className='source-toggle-code'> + <div className='source-toggle-code-tabs'> + {Object.keys(sources).map((source, i) => ( + <div + key={i} + className={`source-toggle-tab${this.state.source === source ? ' selected' : ''}`} + onClick={() => this.setState({source})}> + {source} + </div> + ))} + </div> + <pre> + <code dangerouslySetInnerHTML={this.renderMarkdown()} /> + </pre> + </div> + </div> + </div> + ); + } +} diff --git a/stories/react/utils/components/DropdownMenu.js b/stories/react/utils/components/DropdownMenu.js new file mode 100644 index 0000000..4a69463 --- /dev/null +++ b/stories/react/utils/components/DropdownMenu.js @@ -0,0 +1,14 @@ +import React from 'react'; + +const DropdownMenu = ({title, value, onChange, options}) => ( + <div className='option-container'> + <label>{title}</label> + <select value={value} onChange={onChange}> + {options.map((option, i) => + <option key={i} value={option}>{option}</option> + )} + </select> + </div> +); + +export default DropdownMenu; diff --git a/stories/react/utils/jsxToString.js b/stories/react/utils/jsxToString.js new file mode 100644 index 0000000..8b799ad --- /dev/null +++ b/stories/react/utils/jsxToString.js @@ -0,0 +1,74 @@ +import React, {Children} from 'react'; + +const INDENT = ' '; + +function stringRepresentationForJsx(item) { + if (typeof item === 'string') { + return `'${item}'`; + } else if (typeof item === 'number') { + return item.toString(); + } else if (Array.isArray(item)) { + return `[${item.map(val => stringRepresentationForJsx(val)).toString()}]`; + } + else if (typeof item === 'boolean') { + return item.toString(); + } + else if (typeof item === 'function') { + return item.toString().replace(/\s{2,}/g, ' '); + } else if (typeof item === 'object') { + let repr = '{'; + for (let key in item) { + if (item.hasOwnProperty(key)) { + repr += `${key}: ${stringRepresentationForJsx(item[key])}, `; + } + } + repr = repr.slice(0, -2); + repr += '}'; + return repr; + } +} + +function parseProps(jsx, indentCount) { + let result = ''; + for (let prop in jsx.props) { + let value = jsx.props[prop]; + if (prop !== 'children' && value) { + let repr = stringRepresentationForJsx(value); + let isString = repr.startsWith("'"); + result += `\n${INDENT.repeat(indentCount)}${prop}`; + if (value !== true) { + result += `=${isString ? '' : '{ '}${stringRepresentationForJsx(value)}${isString ? '' : ' }'}`; + } + } + } + return result; +} + +function jsxToString({jsx, indentCount=0, exclude}) { + if (typeof jsx === 'string'){ + return jsx; + } + + let name = typeof jsx.type === 'string' ? jsx.type : jsx.type.name; + let result = name === exclude ? '' + : `${INDENT.repeat(indentCount)}<${name}${parseProps(jsx, indentCount + 1)}`; + + if (jsx.props.hasOwnProperty('children')) { + let {children} = jsx.props; + let childrenArr = Children.toArray(children); + if (name !== exclude) { result += '>\n';} + if (typeof children === 'string') { + result += `${INDENT.repeat(indentCount + 1)}${children}\n`; + } else { + let newIndentCount = name === exclude ? indentCount : indentCount + 1; + childrenArr.forEach(child => result += `${jsxToString({jsx: child, indentCount: newIndentCount})}\n`); + } + const closingTag = name === exclude ? '' + : `${INDENT.repeat(indentCount)}</${name}>`; + return result + closingTag; + } + + return result + ' />'; +} + +export default jsxToString; |