@platformatic/ui-components 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +26 -0
  3. package/fonts/Montserrat/Montserrat-Italic-VariableFont_wght.ttf +0 -0
  4. package/fonts/Montserrat/Montserrat-VariableFont_wght.ttf +0 -0
  5. package/fonts/Montserrat/OFL.txt +93 -0
  6. package/fonts/Montserrat/README.txt +81 -0
  7. package/fonts/Montserrat/static/Montserrat-Black.ttf +0 -0
  8. package/fonts/Montserrat/static/Montserrat-BlackItalic.ttf +0 -0
  9. package/fonts/Montserrat/static/Montserrat-Bold.ttf +0 -0
  10. package/fonts/Montserrat/static/Montserrat-BoldItalic.ttf +0 -0
  11. package/fonts/Montserrat/static/Montserrat-ExtraBold.ttf +0 -0
  12. package/fonts/Montserrat/static/Montserrat-ExtraBoldItalic.ttf +0 -0
  13. package/fonts/Montserrat/static/Montserrat-ExtraLight.ttf +0 -0
  14. package/fonts/Montserrat/static/Montserrat-ExtraLightItalic.ttf +0 -0
  15. package/fonts/Montserrat/static/Montserrat-Italic.ttf +0 -0
  16. package/fonts/Montserrat/static/Montserrat-Light.ttf +0 -0
  17. package/fonts/Montserrat/static/Montserrat-LightItalic.ttf +0 -0
  18. package/fonts/Montserrat/static/Montserrat-Medium.ttf +0 -0
  19. package/fonts/Montserrat/static/Montserrat-MediumItalic.ttf +0 -0
  20. package/fonts/Montserrat/static/Montserrat-Regular.ttf +0 -0
  21. package/fonts/Montserrat/static/Montserrat-SemiBold.ttf +0 -0
  22. package/fonts/Montserrat/static/Montserrat-SemiBoldItalic.ttf +0 -0
  23. package/fonts/Montserrat/static/Montserrat-Thin.ttf +0 -0
  24. package/fonts/Montserrat/static/Montserrat-ThinItalic.ttf +0 -0
  25. package/index.html +12 -0
  26. package/index.js +28 -0
  27. package/package.json +48 -0
  28. package/postcss.config.cjs +8 -0
  29. package/public/api-icon-closed.svg +6 -0
  30. package/public/api-icon.svg +9 -0
  31. package/public/vite.svg +1 -0
  32. package/src/App.jsx +12 -0
  33. package/src/components/Api/Endpoints.jsx +14 -0
  34. package/src/components/Api/Footer.jsx +24 -0
  35. package/src/components/Api/Footer.module.css +3 -0
  36. package/src/components/Api/Status.jsx +19 -0
  37. package/src/components/Api/Version.jsx +8 -0
  38. package/src/components/Api.jsx +83 -0
  39. package/src/components/Api.module.css +11 -0
  40. package/src/components/ApiDetail.jsx +106 -0
  41. package/src/components/BorderedBox.jsx +15 -0
  42. package/src/components/BorderedBox.module.css +3 -0
  43. package/src/components/BorderedText.jsx +8 -0
  44. package/src/components/BorderedText.module.css +3 -0
  45. package/src/components/Common.module.css +7 -0
  46. package/src/components/HorizontalSeparator.jsx +5 -0
  47. package/src/components/Main.jsx +69 -0
  48. package/src/components/Playground.jsx +9 -0
  49. package/src/components/Pr.jsx +9 -0
  50. package/src/components/SearchBar.jsx +25 -0
  51. package/src/components/SearchBar.module.css +6 -0
  52. package/src/components/StatValue.jsx +16 -0
  53. package/src/components/StatView.jsx +47 -0
  54. package/src/components/StatsView.module.css +10 -0
  55. package/src/components/TabbedWindow.jsx +34 -0
  56. package/src/components/TabbedWindow.module.css +10 -0
  57. package/src/components/Versions.jsx +9 -0
  58. package/src/components/VerticalSeparator.jsx +5 -0
  59. package/src/components/layouts/Layout.jsx +10 -0
  60. package/src/components/layouts/TwoColumnsLayout.jsx +9 -0
  61. package/src/lib/utils.js +20 -0
  62. package/src/main.jsx +9 -0
  63. package/src/stories/BorderedBox.stories.jsx +19 -0
  64. package/src/stories/Button.jsx +50 -0
  65. package/src/stories/Button.stories.jsx +40 -0
  66. package/src/stories/Header.jsx +57 -0
  67. package/src/stories/Header.stories.jsx +24 -0
  68. package/src/stories/Introduction.stories.mdx +211 -0
  69. package/src/stories/Page.jsx +69 -0
  70. package/src/stories/Page.stories.jsx +25 -0
  71. package/src/stories/assets/code-brackets.svg +1 -0
  72. package/src/stories/assets/colors.svg +1 -0
  73. package/src/stories/assets/comments.svg +1 -0
  74. package/src/stories/assets/direction.svg +1 -0
  75. package/src/stories/assets/flow.svg +1 -0
  76. package/src/stories/assets/plugin.svg +1 -0
  77. package/src/stories/assets/repo.svg +1 -0
  78. package/src/stories/assets/stackalt.svg +1 -0
  79. package/src/stories/button.css +30 -0
  80. package/src/stories/header.css +32 -0
  81. package/src/stories/page.css +69 -0
  82. package/src/styles/main.css +22 -0
  83. package/tailwind.config.cjs +27 -0
  84. package/vite.config.js +15 -0
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3
+ import { faCircle } from '@fortawesome/free-solid-svg-icons'
4
+ export default function ApiStatus (props) {
5
+ const { online } = props
6
+ const colorClass = online ? 'text-main-green' : 'text-error-red'
7
+ return (
8
+ <div className={colorClass}>
9
+ <FontAwesomeIcon icon={faCircle} size='2xs' className={`text-${colorClass} mr-1`} />
10
+ {online && (
11
+ 'Online'
12
+ )}
13
+
14
+ {!online && (
15
+ 'Offline'
16
+ )}
17
+ </div>
18
+ )
19
+ }
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ import BorderedText from '../BorderedText'
4
+
5
+ export default function ApiVersion (props) {
6
+ const { version } = props
7
+ return (<div className='font-light'><BorderedText text={`v${version}`} /></div>)
8
+ }
@@ -0,0 +1,83 @@
1
+ 'use strict'
2
+
3
+ import ApiFooter from './Api/Footer'
4
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
5
+ import { faLink } from '@fortawesome/free-solid-svg-icons'
6
+ import ApiVersion from './Api/Version'
7
+ import Separator from './VerticalSeparator'
8
+ import ApiStatus from './Api/Status'
9
+ import BorderedBox from './BorderedBox'
10
+ import HorizontalSeparator from './HorizontalSeparator'
11
+ import styles from './Api.module.css'
12
+ import { useState } from 'react'
13
+
14
+ function ApiName (props) {
15
+ const { name } = props
16
+ return (
17
+ <span className='text-main-green font-semibold text-2xl'>{name}</span>
18
+ )
19
+ }
20
+ export default function API (props) {
21
+ const [isOpen, setIsOpen] = useState(true)
22
+ const { name, version, source, lastDeploy, graphql, openapi, online } = props.data
23
+ const color = online ? 'green' : 'red'
24
+ function getApiIcon () {
25
+ if (isOpen) {
26
+ return 'api-icon.svg'
27
+ }
28
+ return 'api-icon-closed.svg'
29
+ }
30
+ return (
31
+ <BorderedBox
32
+ color={color}
33
+ >
34
+ {isOpen && (
35
+ <>
36
+ <div className={styles.main}>
37
+ <div className='flex w-full'>
38
+ <img className='w-9 mr-5' src={getApiIcon()} />
39
+ <div className={styles.data}>
40
+ <div className='flex items-center justify-between'>
41
+ <div className='flex items-center justify-between gap-4'>
42
+ <ApiName name={name} />
43
+ <span>
44
+ <a href='#' className='border p-2 rounded-full'>
45
+ <FontAwesomeIcon color='white' icon={faLink} />
46
+ </a>
47
+ </span>
48
+ <ApiVersion version={version} />
49
+ </div>
50
+ <div className={styles.status}>
51
+ <ApiStatus online={online} />
52
+ </div>
53
+
54
+ </div>
55
+
56
+ <div className='flex gap-2 items-center'>
57
+ <span>Generated with: {source}</span>
58
+ <Separator />
59
+ <span>Last deployed: {lastDeploy}</span>
60
+ </div>
61
+ </div>
62
+
63
+ </div>
64
+
65
+ </div>
66
+ <HorizontalSeparator />
67
+ <ApiFooter
68
+ graphql={graphql}
69
+ openapi={openapi}
70
+ />
71
+ </>
72
+ )}
73
+ {!isOpen && (
74
+ <div className='flex items-center'>
75
+ <img className='w-9 mr-5' src={getApiIcon()} />
76
+ <ApiName name={name} />
77
+ <span onClick={() => { setIsOpen(!isOpen) }} className='ml-4 hover:cursor-pointer'>Click here to set up your api</span>
78
+ </div>
79
+
80
+ )}
81
+ </BorderedBox>
82
+ )
83
+ }
@@ -0,0 +1,11 @@
1
+ .main {
2
+ @apply flex items-center text-white;
3
+ }
4
+
5
+ .data {
6
+ @apply flex flex-col w-full;
7
+ }
8
+
9
+ .status {
10
+ @apply justify-self-end;
11
+ }
@@ -0,0 +1,106 @@
1
+ 'use strict'
2
+
3
+ import Endpoints from './Api/Endpoints'
4
+ import BorderedBox from './BorderedBox'
5
+ import BorderedText from './BorderedText'
6
+ import HorizontalSeparator from './HorizontalSeparator'
7
+ import TwoColumnsLayout from './layouts/TwoColumnsLayout'
8
+ import StatsView from './StatView'
9
+ import VerticalSeparator from './VerticalSeparator'
10
+
11
+ export default function ApiDetails (props) {
12
+ const apiData = props.data
13
+ const { url, repository, version, openapi, graphql, source, lastDeploy } = apiData
14
+ const requestStats = {
15
+ mainStat: {
16
+ value: 12,
17
+ label: 'Total',
18
+ color: 'green'
19
+ },
20
+ otherStats: [
21
+ {
22
+ value: 8,
23
+ label: 'per minute',
24
+ color: 'green'
25
+ },
26
+ {
27
+ value: 0.5,
28
+ label: 'per second',
29
+ color: 'green'
30
+ }
31
+ ]
32
+ }
33
+
34
+ const latencyStats = {
35
+ mainStat: {
36
+ value: 150,
37
+ label: 'ms',
38
+ color: 'green',
39
+ pre: 'P90'
40
+ },
41
+ otherStats: [
42
+ {
43
+ value: 600,
44
+ label: 'ms',
45
+ color: 'green',
46
+ pre: 'P99'
47
+ },
48
+ {
49
+ value: 4.42,
50
+ label: 's',
51
+ color: 'green',
52
+ pre: 'P95'
53
+ }
54
+ ]
55
+ }
56
+ return (
57
+ <>
58
+ <BorderedBox classes='api-data'>
59
+ <div>
60
+ Preview/Staging URL: <a href={url}>{url}</a>
61
+ </div>
62
+ <div>
63
+ Repository: <a href={`https://github.com/${repository}`}>{repository}</a>
64
+ </div>
65
+ <div>
66
+ Version: <BorderedText text={`v${version}`} />
67
+ </div>
68
+ <HorizontalSeparator />
69
+ <div className='flex gap-2 items-center'>
70
+ <span>Generated with: {source}</span>
71
+ <VerticalSeparator />
72
+ <span>Last deployed: {lastDeploy}</span>
73
+ </div>
74
+ <div className='flex items-center'>
75
+ <span className='mr-2'>Endpoints: </span><Endpoints graphql={graphql} openapi={openapi} />
76
+ </div>
77
+ </BorderedBox>
78
+ <TwoColumnsLayout>
79
+ <BorderedBox>
80
+ <StatsView
81
+ title='Requests'
82
+ tooltip='Number of requests'
83
+ stats={requestStats}
84
+ />
85
+ </BorderedBox>
86
+ <BorderedBox>
87
+ <StatsView
88
+ title='Latency'
89
+ tooltip='Request Latency'
90
+ stats={latencyStats}
91
+ />
92
+ </BorderedBox>
93
+ </TwoColumnsLayout>
94
+
95
+ <TwoColumnsLayout>
96
+ <BorderedBox>
97
+ Connected Clients
98
+ </BorderedBox>
99
+ <BorderedBox>
100
+ Failure Rate
101
+ </BorderedBox>
102
+ </TwoColumnsLayout>
103
+ </>
104
+
105
+ )
106
+ }
@@ -0,0 +1,15 @@
1
+ 'use strict'
2
+
3
+ import { getColor } from '../lib/utils'
4
+ import styles from './BorderedBox.module.css'
5
+ import commonStyles from './Common.module.css'
6
+ export default function BorderedBox (props) {
7
+ const { classes, color, children } = props
8
+ const borderColor = getColor('border', color)
9
+
10
+ return (
11
+ <div className={`${styles.borderedBox} ${commonStyles.bordered} ${classes} ${borderColor}`}>
12
+ {children}
13
+ </div>
14
+ )
15
+ }
@@ -0,0 +1,3 @@
1
+ .borderedBox {
2
+ @apply rounded-md p-5 bg-dark-blue text-white mb-4 flex-1 gap-y-4;
3
+ }
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+ import styles from './BorderedText.module.css'
3
+
4
+ export default function BorderedText (props) {
5
+ return (
6
+ <span className={styles.bordered}>{props.text}</span>
7
+ )
8
+ }
@@ -0,0 +1,3 @@
1
+ .bordered {
2
+ @apply border border-solid rounded-md px-2 py-1 border-white tracking-[.25rem] text-white;
3
+ }
@@ -0,0 +1,7 @@
1
+ .padded {
2
+ @apply px-2 py-2.5;
3
+ }
4
+
5
+ .bordered {
6
+ @apply border border-solid box-border rounded-md;
7
+ }
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ export default function HorizontalSeparator () {
4
+ return <hr className='text-dark-green my-4' />
5
+ }
@@ -0,0 +1,69 @@
1
+ 'use strict'
2
+
3
+ import SearchBar from './SearchBar'
4
+ import Api from './Api'
5
+ import ApiDetails from './ApiDetail'
6
+ import TabbedWindow from './TabbedWindow'
7
+ import Prs from './Pr'
8
+ import Playground from './Playground'
9
+ import Versions from './Versions'
10
+ import { useState } from 'react'
11
+ export default function Main () {
12
+ const [currentSearch, setCurrentSearch] = useState('')
13
+ const apis = [
14
+ {
15
+ id: 1,
16
+ name: 'api-online',
17
+ version: '1.2.3',
18
+ source: 'Platformatic DB',
19
+ lastDeploy: '2022-10-10',
20
+ graphql: true,
21
+ openapi: true,
22
+ online: true,
23
+ url: 'https://example.com',
24
+ repository: 'platformatic/example'
25
+ },
26
+ {
27
+ id: 2,
28
+ name: 'api-offline',
29
+ version: '1.2.3',
30
+ source: 'Platformatic DB',
31
+ lastDeploy: '2022-10-10',
32
+ graphql: true,
33
+ openapi: true,
34
+ online: false
35
+ }
36
+ ]
37
+ const tabs = [
38
+ {
39
+ label: 'Overview',
40
+ component: () => <ApiDetails data={apis[0]} />
41
+ },
42
+ {
43
+ label: 'PRs',
44
+ component: () => <Prs />
45
+ },
46
+ {
47
+ label: 'Playground',
48
+ component: () => <Playground />
49
+ },
50
+ {
51
+ label: 'Versions',
52
+ component: () => <Versions />
53
+ }
54
+
55
+ ]
56
+ return (
57
+ <>
58
+ <h1 className='text-white text-2xl font-bold'>My APIs</h1>
59
+ <SearchBar action={(value) => setCurrentSearch(value)} />
60
+ {currentSearch !== '' && (
61
+ <span className='text-main-green'>You searched for {currentSearch}</span>
62
+ )}
63
+ <TabbedWindow
64
+ tabs={tabs}
65
+ />
66
+ {apis.map((api) => { return (<Api key={api.id} data={api} />) })}
67
+ </>
68
+ )
69
+ }
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ import BorderedBox from './BorderedBox'
4
+
5
+ export default function Playground () {
6
+ return (
7
+ <BorderedBox color='green'>Playground</BorderedBox>
8
+ )
9
+ }
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ import BorderedBox from './BorderedBox'
4
+
5
+ export default function Prs () {
6
+ return (
7
+ <BorderedBox color='green'>PR</BorderedBox>
8
+ )
9
+ }
@@ -0,0 +1,25 @@
1
+ 'use strict'
2
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3
+ import { faSearch } from '@fortawesome/free-solid-svg-icons'
4
+ import { useRef } from 'react'
5
+ import styles from './SearchBar.module.css'
6
+ import commonStyles from './Common.module.css'
7
+ export default function SearchBar (props) {
8
+ const inputRef = useRef()
9
+ const { action } = props
10
+
11
+ function handleSearch () {
12
+ const value = inputRef.current.value
13
+ return action(value)
14
+ }
15
+ return (
16
+ <div className={`${styles.input} ${commonStyles.padded}`}>
17
+ <input type='text' placeholder='Search' className='grow' ref={inputRef} />
18
+ <button onClick={handleSearch}>
19
+ <FontAwesomeIcon color='white' icon={faSearch} />
20
+ </button>
21
+
22
+ </div>
23
+
24
+ )
25
+ }
@@ -0,0 +1,6 @@
1
+ .input {
2
+ @apply flex justify-between w-full h-10 text-white border-main-green border border-solid box-border rounded-md;
3
+ }
4
+ .input > input:focus {
5
+ @apply outline-none;
6
+ }
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+ import { Fragment } from 'react'
3
+ import { getColor } from '../lib/utils'
4
+ export default function StatValue (props) {
5
+ const { value, color, label, pre } = props
6
+ return (
7
+ <>
8
+ {pre && <span>{pre}</span>}
9
+ <span className={`text-5xl ${getColor('text', color)}`}>
10
+ {value}
11
+ </span>
12
+ <span>{label}</span>
13
+ </>
14
+
15
+ )
16
+ }
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
+ import { faCircleInfo } from '@fortawesome/free-solid-svg-icons'
5
+ import styles from './StatsView.module.css'
6
+ import HorizontalSeparator from './HorizontalSeparator'
7
+ import VerticalSeparator from './VerticalSeparator'
8
+ import StatValue from './StatValue'
9
+
10
+ export default function StatsView (props) {
11
+ const { title, stats } = props
12
+ const mainStat = stats.mainStat
13
+ const otherStats = stats.otherStats
14
+ return (
15
+ <>
16
+ <div className={styles['stats-header']}>
17
+ <span className='text-xl'>{title}</span>
18
+ <FontAwesomeIcon icon={faCircleInfo} className='text-white' />
19
+ </div>
20
+ <div className={styles['stats-content']}>
21
+ {mainStat && (
22
+ <div className={styles['stats-main-stat']}>
23
+ <StatValue pre={mainStat.pre} label={mainStat.label} color={mainStat.color} value={mainStat.value} />
24
+ </div>
25
+ )}
26
+ {otherStats && (
27
+ <>
28
+ <HorizontalSeparator />
29
+ <div className='flex items-center justify-around'>
30
+ <div className={styles['stats-main-stat']}>
31
+ <StatValue pre={otherStats[0].pre} label={otherStats[0].label} color={otherStats[0].color} value={otherStats[0].value} />
32
+ </div>
33
+
34
+ <VerticalSeparator />
35
+ <div className={styles['stats-main-stat']}>
36
+ <StatValue pre={otherStats[1].pre} label={otherStats[1].label} color={otherStats[1].color} value={otherStats[1].value} />
37
+ </div>
38
+ </div>
39
+
40
+ </>
41
+
42
+ )}
43
+ </div>
44
+
45
+ </>
46
+ )
47
+ }
@@ -0,0 +1,10 @@
1
+ .stats-header {
2
+ @apply flex justify-between items-center font-bold
3
+ }
4
+ .stats-main-stat {
5
+ @apply flex justify-center items-center;
6
+ gap: 1rem;
7
+ }
8
+ .stats-other-stats {
9
+
10
+ }
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+ import React, { useEffect, useState } from 'react'
3
+ import styles from './TabbedWindow.module.css'
4
+
5
+ export default function TabbedWindow (props) {
6
+ const [selected, setSelected] = useState(0)
7
+ const { tabs } = props
8
+ const headers = []
9
+ const components = []
10
+ tabs.forEach((tab) => {
11
+ headers.push(tab.label)
12
+ components.push(tab.component)
13
+ })
14
+ let currentComponent = components[selected]
15
+
16
+ useEffect(() => {
17
+ currentComponent = components[selected]
18
+ }, [selected])
19
+
20
+ return (
21
+ <div className={styles['tabbed-container']}>
22
+ <div className={styles['tabs-header']}>
23
+ {headers.map((header, index) => {
24
+ return (
25
+ <span onClick={() => setSelected(index)} key={index} className={selected === index ? styles['selected-tab'] : ''}>
26
+ {header}
27
+ </span>
28
+ )
29
+ })}
30
+ </div>
31
+ <div className={styles['tabs-content']}>{currentComponent()}</div>
32
+ </div>
33
+ )
34
+ }
@@ -0,0 +1,10 @@
1
+ .tabbed-container {
2
+ @apply bg-main-dark-blue;
3
+ }
4
+
5
+ .tabs-header {
6
+ @apply flex justify-between text-white uppercase hover:cursor-pointer mb-4 tracking-[.25rem] h-[4rem];
7
+ }
8
+ .selected-tab {
9
+ @apply underline text-main-green underline-offset-8 font-bold;
10
+ }
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ import BorderedBox from './BorderedBox'
4
+
5
+ export default function Versions () {
6
+ return (
7
+ <BorderedBox color='green'>Versions</BorderedBox>
8
+ )
9
+ }
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ export default function VerticalSeparator () {
4
+ return <span className='text-dark-green m-3'> | </span>
5
+ }
@@ -0,0 +1,10 @@
1
+ 'use strict'
2
+
3
+ export default function Layout (props) {
4
+ return (
5
+ <div className='container mx-auto px-4 my-5 flex flex-col gap-10'>
6
+ {props.children}
7
+ </div>
8
+
9
+ )
10
+ }
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ export default function TwoColumnsLayout (props) {
4
+ return (
5
+ <div className='grid grid-cols-2 gap-x-4'>
6
+ {props.children}
7
+ </div>
8
+ )
9
+ }
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ function getColor (type, color) {
4
+ let convertedColor
5
+ switch (color) {
6
+ case 'green':
7
+ convertedColor = 'main-green'
8
+ break
9
+ case 'red':
10
+ convertedColor = 'error-red'
11
+ break
12
+ default:
13
+ convertedColor = 'main-green'
14
+ break
15
+ }
16
+ return `${type}-${convertedColor}`
17
+ }
18
+ export {
19
+ getColor
20
+ }
package/src/main.jsx ADDED
@@ -0,0 +1,9 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App'
4
+ import './styles/main.css'
5
+ ReactDOM.createRoot(document.getElementById('root')).render(
6
+ <React.StrictMode>
7
+ <App />
8
+ </React.StrictMode>
9
+ )
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+ import BorderedBox from '../components/BorderedBox'
3
+ export default {
4
+ title: 'Example/BorderedBox',
5
+ component: BorderedBox,
6
+ // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
7
+ argTypes: {
8
+ color: { control: 'color' }
9
+ }
10
+ }
11
+
12
+ // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
13
+ const Template = (args) => <BorderedBox {...args}>Hello Platformatic</BorderedBox>
14
+
15
+ export const Green = Template.bind({})
16
+ // More on args: https://storybook.js.org/docs/react/writing-stories/args
17
+ Green.args = {
18
+ color: 'green'
19
+ }
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import './button.css';
4
+
5
+ /**
6
+ * Primary UI component for user interaction
7
+ */
8
+ export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
9
+ const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
10
+ return (
11
+ <button
12
+ type="button"
13
+ className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
14
+ style={backgroundColor && { backgroundColor }}
15
+ {...props}
16
+ >
17
+ {label}
18
+ </button>
19
+ );
20
+ };
21
+
22
+ Button.propTypes = {
23
+ /**
24
+ * Is this the principal call to action on the page?
25
+ */
26
+ primary: PropTypes.bool,
27
+ /**
28
+ * What background color to use
29
+ */
30
+ backgroundColor: PropTypes.string,
31
+ /**
32
+ * How large should the button be?
33
+ */
34
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
35
+ /**
36
+ * Button contents
37
+ */
38
+ label: PropTypes.string.isRequired,
39
+ /**
40
+ * Optional click handler
41
+ */
42
+ onClick: PropTypes.func,
43
+ };
44
+
45
+ Button.defaultProps = {
46
+ backgroundColor: null,
47
+ primary: false,
48
+ size: 'medium',
49
+ onClick: undefined,
50
+ };