@platformatic/ui-components 0.1.10 → 0.1.12

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.
package/index.js CHANGED
@@ -12,6 +12,7 @@ import TwoColumnsLayout from './src/components/layouts/TwoColumnsLayout'
12
12
  import Playground from './src/components/Playground'
13
13
  import PullRequest from './src/components/PullRequest'
14
14
  import SearchBar from './src/components/SearchBar'
15
+ import Status from './src/components/Status'
15
16
  import TabbedWindow from './src/components/TabbedWindow'
16
17
  import TextWithLabel from './src/components/TextWithLabel'
17
18
  import Versions from './src/components/Versions'
@@ -29,6 +30,7 @@ export {
29
30
  Playground,
30
31
  PullRequest,
31
32
  SearchBar,
33
+ Status,
32
34
  TabbedWindow,
33
35
  TextWithLabel,
34
36
  Versions,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@platformatic/ui-components",
3
3
  "description": "Platformatic UI Components",
4
- "version": "0.1.10",
4
+ "version": "0.1.12",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "scripts": {
@@ -12,7 +12,9 @@
12
12
  "build-storybook": "NODE_OPTIONS='--openssl-legacy-provider' build-storybook",
13
13
  "tailwind:watch": "tailwindcss -i ./src/styles/main.css -o ./dist/main.css --watch",
14
14
  "tailwind": "tailwindcss -i ./src/styles/main.css -o ./dist/main.css",
15
- "lint": "standard --fix"
15
+ "lint": "standard src | snazzy",
16
+ "lint:fix": "standard src --fix",
17
+ "test": "vitest run"
16
18
  },
17
19
  "dependencies": {
18
20
  "@fortawesome/fontawesome-svg-core": "^6.2.0",
@@ -34,17 +36,28 @@
34
36
  "@storybook/builder-vite": "^0.2.4",
35
37
  "@storybook/react": "^6.5.12",
36
38
  "@storybook/testing-library": "^0.0.13",
39
+ "@testing-library/jest-dom": "^5.16.5",
40
+ "@testing-library/react": "^13.4.0",
37
41
  "@types/react": "^18.0.17",
38
42
  "@types/react-dom": "^18.0.6",
39
43
  "@vitejs/plugin-react": "^2.1.0",
40
44
  "babel-loader": "^8.2.5",
45
+ "jsdom": "^20.0.1",
46
+ "react-test-renderer": "^18.2.0",
47
+ "snazzy": "^9.0.0",
41
48
  "standard": "^17.0.0",
42
49
  "tailwindcss": "^3.1.8",
43
- "vite": "^3.1.0"
50
+ "vite": "^3.1.0",
51
+ "vitest": "^0.24.3"
44
52
  },
45
53
  "standard": {
46
- "global": [
54
+ "globals": [
55
+ "expect",
56
+ "test",
47
57
  "alert"
58
+ ],
59
+ "ignore": [
60
+ "/dist"
48
61
  ]
49
62
  }
50
63
  }
@@ -8,7 +8,7 @@ import ApiIcon from './icons/ApiIcon'
8
8
  import ApiIconClosed from './icons/ApiIconClosed'
9
9
  import VerticalSeparator from './VerticalSeparator'
10
10
  import TextWithLabel from './TextWithLabel'
11
- import ApiStatus from './Api/Status'
11
+ import Status from './Status'
12
12
  import BorderedBox from './BorderedBox'
13
13
  import HorizontalSeparator from './HorizontalSeparator'
14
14
  import styles from './Api.module.css'
@@ -41,7 +41,7 @@ export default function ApiSummary (props) {
41
41
  <ApiVersion version={version} />
42
42
  </div>
43
43
  <div className={styles.status}>
44
- <ApiStatus online={online} />
44
+ <Status color={color} status={online ? 'online' : 'offline'} />
45
45
  </div>
46
46
 
47
47
  </div>
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import PropTypes from 'prop-types'
3
2
  import LoginButton from './LoginButton'
4
3
  import { faGithub } from '@fortawesome/free-brands-svg-icons'
5
4
 
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import PropTypes from 'prop-types'
3
2
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
3
  import styles from './LoginButton.module.css'
5
4
 
@@ -8,9 +7,10 @@ export default function LoginButton ({ icon, label, onClick, ...props }) {
8
7
  <div className={styles.container} onClick={onClick}>
9
8
  <div
10
9
  className={styles.button}
10
+ data-testid='login-button'
11
11
  {...props}
12
12
  >
13
- {icon ? <FontAwesomeIcon icon={icon} className='mr-2' /> : null} {label}
13
+ {icon ? <FontAwesomeIcon icon={icon} className='mr-2' data-testid='login-button-icon' /> : null} {label}
14
14
  </div>
15
15
  </div>
16
16
  )
@@ -0,0 +1,25 @@
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import '@testing-library/jest-dom/extend-expect'
4
+ import LoginButton from './LoginButton'
5
+ import { faTwitter } from '@fortawesome/free-brands-svg-icons'
6
+
7
+ test('<LoginButton /> with label', () => {
8
+ const label = 'My Button'
9
+ render(
10
+ <LoginButton label={label} />
11
+ )
12
+ expect(screen.getByTestId('login-button')).toHaveTextContent(label)
13
+ const icon = screen.queryByTestId('login-button-icon')
14
+ expect(icon).not.toBeInTheDocument()
15
+ })
16
+
17
+ test('<LoginButton /> with label and twitter icon', () => {
18
+ const label = 'My Twitter Button'
19
+ render(
20
+ <LoginButton label={label} icon={faTwitter} />
21
+ )
22
+ expect(screen.getByTestId('login-button')).toHaveTextContent(label)
23
+ const icon = screen.queryByTestId('login-button-icon')
24
+ expect(icon).toBeInTheDocument()
25
+ })
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  import SearchBar from './SearchBar'
4
- import Api from './Api'
5
4
  import ApiDetails from './ApiDetail'
5
+ import ApiSummary from './ApiSummary'
6
6
  import TabbedWindow from './TabbedWindow'
7
7
  import Prs from './PullRequest'
8
8
  import Playground from './Playground'
@@ -63,7 +63,7 @@ export default function Main () {
63
63
  <TabbedWindow
64
64
  tabs={tabs}
65
65
  />
66
- {apis.map((api) => { return (<Api key={api.id} data={api} />) })}
66
+ {apis.map((api) => { return (<ApiSummary key={api.id} data={api} />) })}
67
67
  </>
68
68
  )
69
69
  }
@@ -6,26 +6,61 @@ import PullRequestIcon from './icons/PullRequestIcon'
6
6
  import styles from './PullRequest.module.css'
7
7
  import TextWithLabel from './TextWithLabel'
8
8
  import VerticalSeparator from './VerticalSeparator'
9
- export default function PullRequest ({ id, title, lastCommit, url }) {
9
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
10
+ import { faStopCircle, faRotateRight } from '@fortawesome/free-solid-svg-icons'
11
+ import Status from './Status'
12
+ export default function PullRequest ({ id, title, lastCommit, url, status, onStop, onReload }) {
13
+ let prStatusColor
14
+ if (status === 'available') {
15
+ prStatusColor = 'white'
16
+ } else if (status === 'running') {
17
+ prStatusColor = 'green'
18
+ } else if (status === 'unavailable') {
19
+ prStatusColor = 'white'
20
+ }
21
+
22
+ function handleStop () {
23
+ if (onStop) {
24
+ onStop()
25
+ }
26
+ }
27
+ function handleReload () {
28
+ if (onReload) {
29
+ onReload()
30
+ }
31
+ }
10
32
  return (
11
- <BorderedBox color='green'>
12
- <div className={styles.header}>
13
- <PullRequestIcon />
14
- <span className={styles.id}>#{id}</span>
15
- <span><a href={url} target='_blank' rel='noreferrer'>{url}</a></span>
16
- </div>
17
- <div className={styles.content}>
18
- <TextWithLabel label='PR Title' text={title} />
19
- </div>
33
+ <div className={status === 'unavailable' ? styles.unavailable : null}>
34
+ <BorderedBox color={prStatusColor}>
35
+ <div className={styles.header}>
36
+ <div className={styles.title}>
37
+ <PullRequestIcon />
38
+ <span className={styles.id}>#{id}</span>
39
+ <span><a href={url} target='_blank' rel='noreferrer'>{url}</a></span>
40
+ </div>
41
+ <div className={styles.status}>
42
+ <Status color={prStatusColor} status={status} />
43
+ <VerticalSeparator />
44
+ <div className={styles.icons}>
45
+ <FontAwesomeIcon icon={faStopCircle} mask={['circle']} onClick={handleStop} />
46
+ <FontAwesomeIcon icon={faRotateRight} mask={['circle']} onClick={handleReload} />
47
+ </div>
48
+ </div>
49
+ </div>
50
+ <div className={styles.content}>
51
+ <TextWithLabel label='PR Title' text={title} />
52
+ </div>
53
+
54
+ <HorizontalSeparator />
55
+ <div className={styles.footer}>
56
+ <TextWithLabel label='Last commit by' text={lastCommit.author} />
57
+ <VerticalSeparator />
58
+ <TextWithLabel label='Commit SHA' text={lastCommit.sha} />
59
+ <VerticalSeparator />
60
+ <TextWithLabel label='Last Update' text={lastCommit.date} />
61
+ </div>
62
+ </BorderedBox>
63
+ </div>
20
64
 
21
- <HorizontalSeparator />
22
- <div className={styles.footer}>
23
- <TextWithLabel label='Last commit by' text={lastCommit.author} />
24
- <VerticalSeparator />
25
- <TextWithLabel label='Commit SHA' text={lastCommit.sha} />
26
- <VerticalSeparator />
27
- <TextWithLabel label='Last Update' text={lastCommit.date} />
28
- </div>
29
- </BorderedBox>
30
65
  )
31
66
  }
@@ -5,8 +5,23 @@
5
5
  @apply text-xl text-main-green font-semibold;
6
6
  }
7
7
  .header {
8
- @apply flex items-center gap-x-4 mb-4;
8
+ @apply flex items-center justify-between mb-4;
9
+ }
10
+ .title {
11
+ @apply flex items-center gap-x-4;
9
12
  }
10
13
  .footer {
11
- @apply flex items-center
14
+ @apply flex items-center;
15
+ }
16
+ .status {
17
+ @apply flex items-center;
18
+ }
19
+ .unavailable {
20
+ @apply opacity-30;
21
+ }
22
+ .icons {
23
+ @apply flex justify-between gap-x-2;
24
+ }
25
+ .icons > *:hover {
26
+ @apply cursor-pointer;
12
27
  }
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+ import React from 'react'
3
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
+ import { faCircle } from '@fortawesome/free-solid-svg-icons'
5
+ export default function Status (props) {
6
+ const { color, status } = props
7
+
8
+ function getColorClass (color) {
9
+ switch (color) {
10
+ case 'green':
11
+ return 'text-main-green'
12
+ case 'red':
13
+ return 'text-error-red'
14
+ case 'white':
15
+ default:
16
+ return 'text-white'
17
+ }
18
+ }
19
+ const colorClass = getColorClass(color)
20
+ return (
21
+ <div className={colorClass}>
22
+ <FontAwesomeIcon icon={faCircle} size='2xs' className={`text-${colorClass} mr-2`} />
23
+ {status}
24
+ </div>
25
+ )
26
+ }
package/src/lib/utils.js CHANGED
@@ -9,6 +9,9 @@ function getColor (type, color) {
9
9
  case 'red':
10
10
  convertedColor = 'error-red'
11
11
  break
12
+ case 'white':
13
+ convertedColor = 'white'
14
+ break
12
15
  default:
13
16
  convertedColor = 'main-green'
14
17
  break
@@ -5,7 +5,10 @@ export default {
5
5
  component: BorderedBox,
6
6
  // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
7
7
  argTypes: {
8
- color: { control: 'color' }
8
+ color: {
9
+ options: ['green', 'red', 'white'],
10
+ control: { type: ' radio' }
11
+ }
9
12
  }
10
13
  }
11
14
 
@@ -13,7 +16,7 @@ export default {
13
16
  const Template = (args) => <BorderedBox {...args}>Hello Platformatic</BorderedBox>
14
17
 
15
18
  export const Green = Template.bind({})
16
- // More on args: https://storybook.js.org/docs/react/writing-stories/args
19
+
17
20
  Green.args = {
18
21
  color: 'green'
19
22
  }
@@ -22,3 +25,9 @@ export const Red = Template.bind({})
22
25
  Red.args = {
23
26
  color: 'red'
24
27
  }
28
+
29
+ export const White = Template.bind({})
30
+
31
+ White.args = {
32
+ color: 'white'
33
+ }
@@ -1,23 +1,23 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import './button.css';
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import './button.css'
4
4
 
5
5
  /**
6
6
  * Primary UI component for user interaction
7
7
  */
8
8
  export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
9
- const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
9
+ const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'
10
10
  return (
11
11
  <button
12
- type="button"
12
+ type='button'
13
13
  className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
14
14
  style={backgroundColor && { backgroundColor }}
15
15
  {...props}
16
16
  >
17
17
  {label}
18
18
  </button>
19
- );
20
- };
19
+ )
20
+ }
21
21
 
22
22
  Button.propTypes = {
23
23
  /**
@@ -39,12 +39,12 @@ Button.propTypes = {
39
39
  /**
40
40
  * Optional click handler
41
41
  */
42
- onClick: PropTypes.func,
43
- };
42
+ onClick: PropTypes.func
43
+ }
44
44
 
45
45
  Button.defaultProps = {
46
46
  backgroundColor: null,
47
47
  primary: false,
48
48
  size: 'medium',
49
- onClick: undefined,
50
- };
49
+ onClick: undefined
50
+ }
@@ -9,7 +9,7 @@ export default {
9
9
  // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
10
10
  argTypes: {
11
11
  backgroundColor: { control: 'color' }
12
- },
12
+ }
13
13
  }
14
14
 
15
15
  // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
@@ -20,21 +20,21 @@ export const Primary = Template.bind({})
20
20
  Primary.args = {
21
21
  primary: true,
22
22
  label: 'Button'
23
- };
23
+ }
24
24
 
25
25
  export const Secondary = Template.bind({})
26
26
  Secondary.args = {
27
27
  label: 'Button'
28
- };
28
+ }
29
29
 
30
30
  export const Large = Template.bind({})
31
31
  Large.args = {
32
32
  size: 'large',
33
33
  label: 'Button'
34
- };
34
+ }
35
35
 
36
36
  export const Small = Template.bind({})
37
37
  Small.args = {
38
38
  size: 'small',
39
39
  label: 'Button'
40
- };
40
+ }
@@ -4,14 +4,13 @@ export default {
4
4
  title: 'Platformatic/PullRequest',
5
5
  component: PullRequest,
6
6
  argTypes: {
7
- color: { control: 'color' }
8
7
  }
9
8
  }
10
9
 
11
- const Template = (args) => <PullRequest {...args}>Hello Platformatic</PullRequest>
10
+ const Template = (args) => <PullRequest {...args} />
12
11
 
13
- export const Standard = Template.bind({})
14
- Standard.args = {
12
+ export const Available = Template.bind({})
13
+ Available.args = {
15
14
  id: '88',
16
15
  title: 'doc: host => hostname, binded to 0.0.0.0 to be more container friendly',
17
16
  url: 'http://example.com',
@@ -19,5 +18,32 @@ Standard.args = {
19
18
  sha: 'bgb3ffe3',
20
19
  author: 'Foobar',
21
20
  date: '2022-10-11'
22
- }
21
+ },
22
+ status: 'available'
23
+ }
24
+
25
+ export const Running = Template.bind({})
26
+ Running.args = {
27
+ id: '88',
28
+ title: 'doc: host => hostname, binded to 0.0.0.0 to be more container friendly',
29
+ url: 'http://example.com',
30
+ lastCommit: {
31
+ sha: 'bgb3ffe3',
32
+ author: 'Foobar',
33
+ date: '2022-10-11'
34
+ },
35
+ status: 'running'
36
+ }
37
+
38
+ export const Unavailable = Template.bind({})
39
+ Unavailable.args = {
40
+ id: '88',
41
+ title: 'doc: host => hostname, binded to 0.0.0.0 to be more container friendly',
42
+ url: 'http://example.com',
43
+ lastCommit: {
44
+ sha: 'bgb3ffe3',
45
+ author: 'Foobar',
46
+ date: '2022-10-11'
47
+ },
48
+ status: 'unavailable'
23
49
  }
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+ import Status from '../components/Status'
3
+
4
+ export default {
5
+ title: 'Platformatic/Status',
6
+ component: Status,
7
+ argTypes: {}
8
+ }
9
+
10
+ const Template = (args) => <Status {...args} />
11
+
12
+ export const White = Template.bind({})
13
+
14
+ White.args = {
15
+ color: 'white',
16
+ status: 'available'
17
+ }
18
+
19
+ export const Green = Template.bind({})
20
+
21
+ Green.args = {
22
+ color: 'green',
23
+ status: 'online'
24
+ }
25
+
26
+ export const Red = Template.bind({})
27
+
28
+ Red.args = {
29
+ color: 'red',
30
+ status: 'offline'
31
+ }
@@ -0,0 +1,14 @@
1
+ /// <reference types="vitest" />
2
+
3
+ import { defineConfig } from 'vite'
4
+ import react from '@vitejs/plugin-react'
5
+
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ test: {
9
+ globals: true,
10
+ environment: 'jsdom',
11
+ exclude: ['node_modules']
12
+ }
13
+
14
+ })
@@ -1,20 +0,0 @@
1
- 'use strict'
2
- import React from 'react'
3
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
- import { faCircle } from '@fortawesome/free-solid-svg-icons'
5
- export default function ApiStatus (props) {
6
- const { online } = props
7
- const colorClass = online ? 'text-main-green' : 'text-error-red'
8
- return (
9
- <div className={colorClass}>
10
- <FontAwesomeIcon icon={faCircle} size='2xs' className={`text-${colorClass} mr-1`} />
11
- {online && (
12
- 'Online'
13
- )}
14
-
15
- {!online && (
16
- 'Offline'
17
- )}
18
- </div>
19
- )
20
- }