react-tab-set 1.2.3 → 2.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-tab-set",
3
- "version": "1.2.3",
3
+ "version": "2.0.0",
4
4
  "description": "A React Tab Set component",
5
5
  "main": "./src/index.mjs",
6
6
  "type": "module",
@@ -31,6 +31,7 @@
31
31
  "devDependencies": {
32
32
  "@babel/core": "^7.22.20",
33
33
  "@babel/eslint-parser": "^7.22.15",
34
+ "@babel/plugin-syntax-jsx": "^7.22.5",
34
35
  "@babel/plugin-transform-typescript": "^7.22.15",
35
36
  "@babel/preset-env": "^7.22.20",
36
37
  "@babel/preset-react": "^7.22.15",
@@ -54,8 +55,8 @@
54
55
  "eslint-plugin-storybook": "^0.6.14",
55
56
  "husky": "^8.0.3",
56
57
  "prop-types": "15.7.2",
57
- "react": "16.14.0",
58
- "react-dom": "16.14.0",
58
+ "react": "^18.2.0",
59
+ "react-dom": "^18.2.0",
59
60
  "storybook": "^7.4.4",
60
61
  "typescript": "^5.2.2"
61
62
  },
package/src/.babelrc CHANGED
@@ -21,6 +21,7 @@
21
21
  "@babel/react"
22
22
  ],
23
23
  "plugins": [
24
+ "@babel/syntax-jsx",
24
25
  "@babel/transform-typescript"
25
26
  ]
26
27
  }
@@ -1,4 +1,9 @@
1
- import React, { Component, Children, cloneElement } from 'react'
1
+ import React, {
2
+ Children,
3
+ cloneElement,
4
+ useState,
5
+ useEffect
6
+ } from 'react'
2
7
  import {
3
8
  v4
4
9
  } from 'uuid'
@@ -10,132 +15,88 @@ import isTabGroup from './is-tab-group.mts'
10
15
  const error = debug('react-tab-set/tab-set')
11
16
 
12
17
  export interface TabSetProps {
13
- onClick: (selectedTab: string) => void
14
- onChange: (selectedTab: string) => void
15
- selectedTab: string
16
18
  children: JSX.Element | JSX.Element[]
17
- }
18
-
19
- export interface TabSetState {
20
19
  selectedTab: string
20
+ onChange: (selectedTab: string) => void
21
21
  }
22
22
 
23
- export default class TabSet extends Component<TabSetProps, TabSetState> {
24
- state = {
25
- selectedTab: this.props.selectedTab
26
- }
27
-
28
- /*
29
- * The selected tab default does not have to be a uuid, but a uuid
30
- * reduces the likelihood that this default has the same value as
31
- * an implemented tab
32
- */
33
- static defaultProps = {
34
- onClick () {},
35
- onChange () {},
36
- selectedTab: v4(),
37
- children: []
38
- }
39
-
40
- static getDerivedStateFromProps ({ selectedTab }: TabSetState): TabSetState {
41
- return {
42
- selectedTab
23
+ function mapChildren (
24
+ children: JSX.Element | JSX.Element[],
25
+ selectedTab: string,
26
+ onTabSelect: (value: React.SetStateAction<string>) => void): JSX.Element[] {
27
+ return Children.map(children, (child) => {
28
+ const { type } = child
29
+
30
+ if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
31
+ const { props } = child
32
+
33
+ if (isTabGroup(type)) {
34
+ return cloneElement(
35
+ child,
36
+ {
37
+ ...props,
38
+ selectedTab,
39
+ onTabSelect
40
+ }
41
+ )
42
+ }
43
+
44
+ if (isTabPanel(type)) {
45
+ return cloneElement(
46
+ child,
47
+ {
48
+ ...props,
49
+ selectedTab
50
+ }
51
+ )
52
+ }
53
+
54
+ const {
55
+ children
56
+ } = props
57
+
58
+ if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
59
+ return cloneElement(
60
+ child,
61
+ {
62
+ ...props,
63
+ children: mapChildren(children, selectedTab, onTabSelect)
64
+ }
65
+ )
66
+ }
43
67
  }
44
- }
45
68
 
46
- shouldComponentUpdate (props: TabSetProps, state: TabSetState): boolean {
47
- return (
48
- props.children !== this.props.children ||
49
- state.selectedTab !== this.state.selectedTab
50
- )
51
- }
69
+ return child
70
+ })
71
+ }
72
+
73
+ export default function TabSet (props: TabSetProps): JSX.Element {
74
+ const {
75
+ children,
76
+ selectedTab: tab
77
+ } = props
78
+
79
+ const [selectedTab, setSelectedTab] = useState(tab)
80
+
81
+ useEffect(() => { setSelectedTab(tab) }, [tab])
52
82
 
53
- handleTabSelect = (selectedTab: string): void => {
54
- const { onClick } = this.props
83
+ useEffect(() => {
84
+ const { onChange } = props
55
85
 
56
86
  try {
57
- onClick(selectedTab)
87
+ onChange(selectedTab)
58
88
  } catch {
59
- error('Error `onClick`')
89
+ error('Error `onChange`')
60
90
  }
91
+ }, [selectedTab])
61
92
 
62
- if (selectedTab !== this.state.selectedTab) {
63
- this.setState({ selectedTab }, () => {
64
- const { onChange } = this.props
65
-
66
- try {
67
- onChange(selectedTab)
68
- } catch {
69
- error('Error `onChange`')
70
- }
71
- })
72
- }
73
- }
74
-
75
- mapChildren (children: JSX.Element | JSX.Element[], selectedTab: string): JSX.Element[] {
76
- return Children.map(children, (child) => {
77
- const { type } = child
78
-
79
- if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
80
- const { props } = child
81
-
82
- if (isTabGroup(type)) {
83
- return cloneElement(
84
- child,
85
- {
86
- ...props,
87
- selectedTab,
88
- onTabSelect: this.handleTabSelect
89
- }
90
- )
91
- }
92
-
93
- if (isTabPanel(type)) {
94
- return cloneElement(
95
- child,
96
- {
97
- ...props,
98
- selectedTab
99
- }
100
- )
101
- }
102
-
103
- const {
104
- children
105
- } = props
106
-
107
- if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
108
- return cloneElement(
109
- child,
110
- {
111
- ...props,
112
- children: this.mapChildren(children, selectedTab)
113
- }
114
- )
115
- }
116
- }
93
+ return (
94
+ <div className='selectedTab-set'>
95
+ {mapChildren(children, selectedTab, setSelectedTab)}
96
+ </div>
97
+ )
98
+ }
117
99
 
118
- return child
119
- })
120
- }
121
-
122
- getChildren (): JSX.Element[] {
123
- const {
124
- children
125
- } = this.props
126
-
127
- const {
128
- selectedTab
129
- } = this.state
130
-
131
- return this.mapChildren(children, selectedTab)
132
- }
133
-
134
- render (): JSX.Element {
135
- return (
136
- <div className='tab-set'>
137
- {this.getChildren()}
138
- </div>
139
- )
140
- }
100
+ TabSet.defaultProps = {
101
+ selectedTab: v4()
141
102
  }
@@ -1,5 +1,5 @@
1
1
  import TabGroup from './tab-group/index.tsx'
2
2
 
3
- export default function isTabGroup (component: any): component is TabGroup {
3
+ export default function isTabGroup (component: any): boolean {
4
4
  return component === TabGroup
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import TabPanel from './tab-panel/index.tsx'
2
2
 
3
- export default function isTabPanel (component: any): component is TabPanel {
3
+ export default function isTabPanel (component: any): boolean {
4
4
  return component === TabPanel
5
5
  }
@@ -1,92 +1,63 @@
1
- import React, { Component, Children, cloneElement } from 'react'
2
- import {
3
- v4
4
- } from 'uuid'
1
+ import React, {
2
+ Children,
3
+ cloneElement
4
+ } from 'react'
5
5
 
6
6
  import isTab from './is-tab.mts'
7
7
 
8
8
  export interface TabGroupProps {
9
- onTabSelect: (selectedTab: string) => void
10
9
  children: JSX.Element | JSX.Element[]
11
10
  selectedTab: string
11
+ onTabSelect: (value: React.SetStateAction<string>) => void
12
12
  }
13
13
 
14
- export default class TabGroup extends Component<TabGroupProps> {
15
- /*
16
- * The selected tab default does not have to be a uuid, but a uuid
17
- * reduces the likelihood that this default has the same value as
18
- * an implemented tab
19
- */
20
- static defaultProps = {
21
- onTabSelect: () => {},
22
- selectedTab: v4(),
23
- children: []
24
- }
25
-
26
- shouldComponentUpdate (props: TabGroupProps): boolean {
27
- return (
28
- props.children !== this.props.children ||
29
- props.selectedTab !== this.props.selectedTab
30
- )
31
- }
32
-
33
- handleTabClick = (tab: string): void => {
34
- const { onTabSelect } = this.props
35
-
36
- onTabSelect(tab)
37
- }
38
-
39
- mapChildren (children: JSX.Element | JSX.Element[], selectedTab: string): JSX.Element[] {
40
- return Children.map(children, (child) => {
41
- const { type } = child
42
-
43
- if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
44
- const { props } = child
45
-
46
- if (isTab(type)) {
47
- return cloneElement(
48
- child,
49
- {
50
- ...props,
51
- selectedTab,
52
- onTabClick: this.handleTabClick
53
- }
54
- )
55
- }
56
-
57
- const {
58
- children
59
- } = props
60
-
61
- if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
62
- return cloneElement(
63
- child,
64
- {
65
- ...props,
66
- children: this.mapChildren(children, selectedTab)
67
- }
68
- )
69
- }
14
+ function mapChildren (children: JSX.Element | JSX.Element[], selectedTab: string, onTabSelect: (value: React.SetStateAction<string>) => void): JSX.Element[] {
15
+ return Children.map(children, (child) => {
16
+ const { type } = child
17
+
18
+ if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
19
+ const { props } = child
20
+
21
+ if (isTab(type)) {
22
+ return cloneElement(
23
+ child,
24
+ {
25
+ ...props,
26
+ selectedTab,
27
+ onTabSelect
28
+ }
29
+ )
70
30
  }
71
31
 
72
- return child
73
- })
74
- }
75
-
76
- getChildren (): JSX.Element[] {
77
- const {
78
- children,
79
- selectedTab
80
- } = this.props
32
+ const {
33
+ children
34
+ } = props
35
+
36
+ if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
37
+ return cloneElement(
38
+ child,
39
+ {
40
+ ...props,
41
+ children: mapChildren(children, selectedTab, onTabSelect)
42
+ }
43
+ )
44
+ }
45
+ }
81
46
 
82
- return this.mapChildren(children, selectedTab)
83
- }
47
+ return child
48
+ })
49
+ }
84
50
 
85
- render (): JSX.Element {
86
- return (
87
- <ul className='tab-group'>
88
- {this.getChildren()}
89
- </ul>
90
- )
91
- }
51
+ export default function TabGroup (props: TabGroupProps): JSX.Element {
52
+ const {
53
+ children,
54
+ selectedTab,
55
+ onTabSelect
56
+ } = props
57
+
58
+ return (
59
+ <ul className='tab-group'>
60
+ {mapChildren(children, selectedTab, onTabSelect)}
61
+ </ul>
62
+ )
92
63
  }
@@ -1,57 +1,31 @@
1
- import React, { Component } from 'react'
2
- import {
3
- v4
4
- } from 'uuid'
1
+ import React from 'react'
5
2
 
6
3
  export interface TabProps {
7
- onTabClick: (tab: string) => void
8
4
  children: JSX.Element | JSX.Element[] | string | number | boolean
9
5
  tab: string
10
6
  selectedTab: string
7
+ onTabSelect: (value: React.SetStateAction<string>) => void
11
8
  }
12
9
 
13
- export default class Tab extends Component<TabProps> {
14
- /*
15
- * The tab and selected tab defaults do not have to be a uuid, but a uuid
16
- * reduces the likelihood that this default has the same value as
17
- * an implemented tab
18
- */
19
- static defaultProps = {
20
- onTabClick: () => {},
21
- tab: v4(),
22
- selectedTab: v4(),
23
- children: []
24
- }
25
-
26
- shouldComponentUpdate (props: TabProps): boolean {
27
- return (
28
- props.children !== this.props.children ||
29
- props.tab !== this.props.tab ||
30
- props.selectedTab !== this.props.selectedTab
31
- )
32
- }
33
-
34
- handleClick = (): void => {
35
- const { tab, onTabClick } = this.props
36
-
37
- onTabClick(tab)
38
- }
39
-
40
- render (): JSX.Element {
41
- const {
42
- tab,
43
- selectedTab,
44
- children
45
- } = this.props
46
-
47
- const className = (tab === selectedTab)
48
- ? 'tab selected'
49
- : 'tab'
50
-
51
- return (
52
- <li className={className} onClick={this.handleClick}>
53
- {children}
54
- </li>
55
- )
56
- }
10
+ export default function Tab (props: TabProps): JSX.Element {
11
+ const {
12
+ children,
13
+ tab,
14
+ selectedTab,
15
+ onTabSelect
16
+ } = props
17
+
18
+ const className = (tab === selectedTab)
19
+ ? 'tab selected'
20
+ : 'tab'
21
+
22
+ return (
23
+ <li
24
+ className={className}
25
+ onClick={(): void => {
26
+ onTabSelect(tab)
27
+ }}>
28
+ {children}
29
+ </li>
30
+ )
57
31
  }
@@ -1,41 +1,23 @@
1
- import React, { Component } from 'react'
2
- import {
3
- v4
4
- } from 'uuid'
1
+ import React from 'react'
5
2
 
6
3
  export interface TabPanelProps {
7
- children: JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
4
+ children?: JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
8
5
  tab: string
9
6
  selectedTab: string
10
7
  render?: () => JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
11
8
  }
12
9
 
13
- export default class TabPanel extends Component<TabPanelProps> {
14
- /*
15
- * The tab and selected tab defaults do not have to be a uuid, but a uuid
16
- * reduces the likelihood that this default has the same value as
17
- * an implemented tab
18
- */
19
- static defaultProps = {
20
- tab: v4(),
21
- selectedTab: v4()
22
- }
23
-
24
- shouldComponentUpdate (props: TabPanelProps): boolean {
25
- if (props.render instanceof Function) return true
10
+ export default function TabPanel (props: TabPanelProps): JSX.Element | null {
11
+ const {
12
+ tab,
13
+ selectedTab
14
+ } = props
26
15
 
27
- return (
28
- props.children !== this.props.children ||
29
- props.tab !== this.props.tab ||
30
- props.selectedTab !== this.props.selectedTab
31
- )
32
- }
33
-
34
- renderPanel (): JSX.Element | null {
16
+ if (tab === selectedTab) {
35
17
  const {
36
18
  render = () => null,
37
19
  children = render()
38
- } = this.props
20
+ } = props
39
21
 
40
22
  if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
41
23
  return (
@@ -44,20 +26,7 @@ export default class TabPanel extends Component<TabPanelProps> {
44
26
  </div>
45
27
  )
46
28
  }
47
-
48
- return null
49
29
  }
50
30
 
51
- render (): JSX.Element | null {
52
- const {
53
- tab,
54
- selectedTab
55
- } = this.props
56
-
57
- if (tab === selectedTab) {
58
- return this.renderPanel()
59
- }
60
-
61
- return null
62
- }
31
+ return null
63
32
  }