react-tab-set 1.2.4 → 2.0.1

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/README.md CHANGED
@@ -47,7 +47,7 @@ Content can be declared either as children or as the return of a function assign
47
47
 
48
48
  If your component extends `PureComponent` or is presentational, you might prefer to declare content as children.
49
49
 
50
- Otherwise, you might prefer to assign a function to the `render` prop, so that rendering is deferred until the tab is selected.
50
+ Otherwise, you can assign a function to the `render` prop, so that rendering is deferred until the tab is selected.
51
51
 
52
52
  ### Content as children
53
53
 
@@ -99,7 +99,7 @@ Similarly, they can be declared in any combination.
99
99
 
100
100
  ## Demonstrating _react-tab-set_
101
101
 
102
- [Example `Storybooks` are available on GitHub](https://github.com/sequencemedia/react-tab-set).
102
+ [Example `Storybooks` are available](https://github.com/sequencemedia/react-tab-set).
103
103
 
104
104
  Clone the repository, then:
105
105
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-tab-set",
3
- "version": "1.2.4",
3
+ "version": "2.0.1",
4
4
  "description": "A React Tab Set component",
5
5
  "main": "./src/index.mjs",
6
6
  "type": "module",
@@ -39,6 +39,7 @@
39
39
  "@babel/register": "^7.22.15",
40
40
  "@sequencemedia/hooks": "^1.0.454",
41
41
  "@storybook/addon-essentials": "^7.4.4",
42
+ "@storybook/addon-links": "^7.4.4",
42
43
  "@storybook/react": "^7.4.4",
43
44
  "@storybook/react-webpack5": "^7.4.4",
44
45
  "@types/debug": "^4.1.9",
@@ -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,89 @@ 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
27
+ ): JSX.Element[] {
28
+ return Children.map(children, (child) => {
29
+ const { type } = child
30
+
31
+ if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
32
+ const { props } = child
33
+
34
+ if (isTabGroup(type)) {
35
+ return cloneElement(
36
+ child,
37
+ {
38
+ ...props,
39
+ selectedTab,
40
+ onTabSelect
41
+ }
42
+ )
43
+ }
44
+
45
+ if (isTabPanel(type)) {
46
+ return cloneElement(
47
+ child,
48
+ {
49
+ ...props,
50
+ selectedTab
51
+ }
52
+ )
53
+ }
54
+
55
+ const {
56
+ children
57
+ } = props
58
+
59
+ if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
60
+ return cloneElement(
61
+ child,
62
+ {
63
+ ...props,
64
+ children: mapChildren(children, selectedTab, onTabSelect)
65
+ }
66
+ )
67
+ }
43
68
  }
44
- }
45
69
 
46
- shouldComponentUpdate (props: TabSetProps, state: TabSetState): boolean {
47
- return (
48
- props.children !== this.props.children ||
49
- state.selectedTab !== this.state.selectedTab
50
- )
51
- }
70
+ return child
71
+ })
72
+ }
73
+
74
+ export default function TabSet (props: TabSetProps): JSX.Element {
75
+ const {
76
+ children,
77
+ selectedTab: tab
78
+ } = props
79
+
80
+ const [selectedTab, setSelectedTab] = useState(tab)
81
+
82
+ useEffect(() => { setSelectedTab(tab) }, [tab])
52
83
 
53
- handleTabSelect = (selectedTab: string): void => {
54
- const { onClick } = this.props
84
+ useEffect(() => {
85
+ const { onChange } = props
55
86
 
56
87
  try {
57
- onClick(selectedTab)
88
+ onChange(selectedTab)
58
89
  } catch {
59
- error('Error `onClick`')
90
+ error('Error `onChange`')
60
91
  }
92
+ }, [selectedTab])
61
93
 
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
- }
94
+ return (
95
+ <div className='selectedTab-set'>
96
+ {mapChildren(children, selectedTab, setSelectedTab)}
97
+ </div>
98
+ )
99
+ }
117
100
 
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
- }
101
+ TabSet.defaultProps = {
102
+ selectedTab: v4()
141
103
  }
@@ -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,4 +1,7 @@
1
- import React, { Component, Children, cloneElement } from 'react'
1
+ import React, {
2
+ Children,
3
+ cloneElement
4
+ } from 'react'
2
5
  import {
3
6
  v4
4
7
  } from 'uuid'
@@ -6,87 +9,67 @@ import {
6
9
  import isTab from './is-tab.mts'
7
10
 
8
11
  export interface TabGroupProps {
9
- onTabSelect: (selectedTab: string) => void
10
12
  children: JSX.Element | JSX.Element[]
11
13
  selectedTab: string
14
+ onTabSelect: (value: React.SetStateAction<string>) => void
12
15
  }
13
16
 
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
- }
17
+ function mapChildren (
18
+ children: JSX.Element | JSX.Element[],
19
+ selectedTab: string,
20
+ onTabSelect: (value: React.SetStateAction<string>) => void
21
+ ): JSX.Element[] {
22
+ return Children.map(children, (child) => {
23
+ const { type } = child
25
24
 
26
- shouldComponentUpdate (props: TabGroupProps): boolean {
27
- return (
28
- props.children !== this.props.children ||
29
- props.selectedTab !== this.props.selectedTab
30
- )
31
- }
25
+ if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
26
+ const { props } = child
32
27
 
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
- }
28
+ if (isTab(type)) {
29
+ return cloneElement(
30
+ child,
31
+ {
32
+ ...props,
33
+ selectedTab,
34
+ onTabSelect
35
+ }
36
+ )
37
+ }
56
38
 
57
- const {
58
- children
59
- } = props
39
+ const {
40
+ children
41
+ } = props
60
42
 
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
- }
43
+ if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
44
+ return cloneElement(
45
+ child,
46
+ {
47
+ ...props,
48
+ children: mapChildren(children, selectedTab, onTabSelect)
49
+ }
50
+ )
70
51
  }
52
+ }
71
53
 
72
- return child
73
- })
74
- }
54
+ return child
55
+ })
56
+ }
75
57
 
76
- getChildren (): JSX.Element[] {
77
- const {
78
- children,
79
- selectedTab
80
- } = this.props
58
+ export default function TabGroup (props: TabGroupProps): JSX.Element {
59
+ const {
60
+ children,
61
+ selectedTab,
62
+ onTabSelect
63
+ } = props
81
64
 
82
- return this.mapChildren(children, selectedTab)
83
- }
65
+ return (
66
+ <ul className='tab-group'>
67
+ {mapChildren(children, selectedTab, onTabSelect)}
68
+ </ul>
69
+ )
70
+ }
84
71
 
85
- render (): JSX.Element {
86
- return (
87
- <ul className='tab-group'>
88
- {this.getChildren()}
89
- </ul>
90
- )
91
- }
72
+ TabGroup.defaultProps = {
73
+ selectedTab: v4(),
74
+ onTabSelect () { }
92
75
  }
@@ -1,57 +1,39 @@
1
- import React, { Component } from 'react'
1
+ import React from 'react'
2
2
  import {
3
3
  v4
4
4
  } from 'uuid'
5
5
 
6
6
  export interface TabProps {
7
- onTabClick: (tab: string) => void
8
7
  children: JSX.Element | JSX.Element[] | string | number | boolean
9
8
  tab: string
10
9
  selectedTab: string
10
+ onTabSelect: (value: React.SetStateAction<string>) => void
11
11
  }
12
12
 
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'
13
+ export default function Tab (props: TabProps): JSX.Element {
14
+ const {
15
+ children,
16
+ tab,
17
+ selectedTab,
18
+ onTabSelect
19
+ } = props
20
+
21
+ const className = (tab === selectedTab)
22
+ ? 'tab selected'
23
+ : 'tab'
24
+
25
+ return (
26
+ <li
27
+ className={className}
28
+ onClick={(): void => {
29
+ onTabSelect(tab)
30
+ }}>
31
+ {children}
32
+ </li>
33
+ )
34
+ }
50
35
 
51
- return (
52
- <li className={className} onClick={this.handleClick}>
53
- {children}
54
- </li>
55
- )
56
- }
36
+ Tab.defaultProps = {
37
+ selectedTab: v4(),
38
+ onTabSelect () { }
57
39
  }
@@ -1,41 +1,26 @@
1
- import React, { Component } from 'react'
1
+ import React from 'react'
2
2
  import {
3
3
  v4
4
4
  } from 'uuid'
5
5
 
6
6
  export interface TabPanelProps {
7
- children: JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
7
+ children?: JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
8
8
  tab: string
9
9
  selectedTab: string
10
10
  render?: () => JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
11
11
  }
12
12
 
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
13
+ export default function TabPanel (props: TabPanelProps): JSX.Element | null {
14
+ const {
15
+ tab,
16
+ selectedTab
17
+ } = props
26
18
 
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 {
19
+ if (tab === selectedTab) {
35
20
  const {
36
21
  render = () => null,
37
22
  children = render()
38
- } = this.props
23
+ } = props
39
24
 
40
25
  if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
41
26
  return (
@@ -44,20 +29,11 @@ export default class TabPanel extends Component<TabPanelProps> {
44
29
  </div>
45
30
  )
46
31
  }
47
-
48
- return null
49
32
  }
50
33
 
51
- render (): JSX.Element | null {
52
- const {
53
- tab,
54
- selectedTab
55
- } = this.props
56
-
57
- if (tab === selectedTab) {
58
- return this.renderPanel()
59
- }
34
+ return null
35
+ }
60
36
 
61
- return null
62
- }
37
+ TabPanel.defaultProps = {
38
+ selectedTab: v4()
63
39
  }