@toptal/picasso-tabs 1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082 → 1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084
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/dist-package/tsconfig.tsbuildinfo +1 -0
- package/package.json +11 -16
- package/src/Tab/Tab.tsx +153 -0
- package/src/Tab/__snapshots__/test.tsx.snap +80 -0
- package/src/Tab/index.ts +6 -0
- package/src/Tab/story/CustomValue.example.tsx +29 -0
- package/src/Tab/story/Disabled.example.tsx +33 -0
- package/src/Tab/story/Icon.example.tsx +52 -0
- package/src/Tab/story/Vertical.example.tsx +83 -0
- package/src/Tab/story/index.jsx +29 -0
- package/src/Tab/styles.ts +106 -0
- package/src/Tab/test.tsx +77 -0
- package/src/TabDescription/TabDescription.tsx +31 -0
- package/src/TabDescription/index.ts +1 -0
- package/src/TabDescription/styles.ts +7 -0
- package/src/TabLabel/TabLabel.tsx +35 -0
- package/src/TabLabel/index.ts +1 -0
- package/src/TabScrollButton/TabScrollButton.tsx +64 -0
- package/src/TabScrollButton/index.ts +6 -0
- package/src/TabScrollButton/styles.ts +37 -0
- package/src/Tabs/Tabs.tsx +81 -0
- package/src/Tabs/__snapshots__/test.tsx.snap +346 -0
- package/src/Tabs/index.ts +6 -0
- package/src/Tabs/story/Default.example.tsx +33 -0
- package/src/Tabs/story/FullWidth.example.tsx +27 -0
- package/src/Tabs/story/ScrollButtons.example.tsx +27 -0
- package/src/Tabs/story/Vertical.example.tsx +84 -0
- package/src/Tabs/story/index.jsx +58 -0
- package/src/Tabs/styles.ts +45 -0
- package/src/Tabs/test.tsx +165 -0
- package/src/Tabs/use-tab-action.ts +27 -0
- package/src/TabsCompound/index.ts +6 -0
- package/src/index.ts +6 -0
- package/tsconfig.json +15 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import { Container, Tabs } from '@toptal/picasso'
|
3
|
+
import { SPACING_4 } from '@toptal/picasso-utils'
|
4
|
+
|
5
|
+
const Example = () => {
|
6
|
+
const [value, setValue] = React.useState(0)
|
7
|
+
|
8
|
+
const handleChange = (_: React.ChangeEvent<{}>, newValue: number) => {
|
9
|
+
setValue(newValue)
|
10
|
+
}
|
11
|
+
|
12
|
+
return (
|
13
|
+
<div>
|
14
|
+
<Tabs value={value} onChange={handleChange}>
|
15
|
+
<Tabs.Tab label='Label' />
|
16
|
+
<Tabs.Tab label='Label' />
|
17
|
+
<Tabs.Tab label='Label' />
|
18
|
+
</Tabs>
|
19
|
+
|
20
|
+
{value === 0 && (
|
21
|
+
<Container top={SPACING_4}>Content of the first tab</Container>
|
22
|
+
)}
|
23
|
+
{value === 1 && (
|
24
|
+
<Container top={SPACING_4}>Content of the second tab</Container>
|
25
|
+
)}
|
26
|
+
{value === 2 && (
|
27
|
+
<Container top={SPACING_4}>Content of the third tab</Container>
|
28
|
+
)}
|
29
|
+
</div>
|
30
|
+
)
|
31
|
+
}
|
32
|
+
|
33
|
+
export default Example
|
@@ -0,0 +1,27 @@
|
|
1
|
+
/* eslint-disable react/no-array-index-key */
|
2
|
+
import React from 'react'
|
3
|
+
import { Container, Tabs } from '@toptal/picasso'
|
4
|
+
import { SPACING_4, palette } from '@toptal/picasso-utils'
|
5
|
+
|
6
|
+
const TAB_COUNT = 2
|
7
|
+
|
8
|
+
const Example = () => {
|
9
|
+
const [value, setValue] = React.useState(0)
|
10
|
+
|
11
|
+
const handleChange = (_: React.ChangeEvent<{}>, newValue: number) => {
|
12
|
+
setValue(newValue)
|
13
|
+
}
|
14
|
+
|
15
|
+
return (
|
16
|
+
<div style={{ width: '45rem', border: `1px solid ${palette.grey.light}` }}>
|
17
|
+
<Tabs value={value} onChange={handleChange} variant='fullWidth'>
|
18
|
+
{Array.from({ length: TAB_COUNT }).map((_, index) => (
|
19
|
+
<Tabs.Tab key={index} label='Label' />
|
20
|
+
))}
|
21
|
+
</Tabs>
|
22
|
+
<Container padded={SPACING_4}>Content of tab #{value}</Container>
|
23
|
+
</div>
|
24
|
+
)
|
25
|
+
}
|
26
|
+
|
27
|
+
export default Example
|
@@ -0,0 +1,27 @@
|
|
1
|
+
/* eslint-disable react/no-array-index-key */
|
2
|
+
import React from 'react'
|
3
|
+
import { Container, Tabs } from '@toptal/picasso'
|
4
|
+
import { SPACING_4 } from '@toptal/picasso-utils'
|
5
|
+
|
6
|
+
const TAB_COUNT = 10
|
7
|
+
|
8
|
+
const Example = () => {
|
9
|
+
const [value, setValue] = React.useState(0)
|
10
|
+
|
11
|
+
const handleChange = (_: React.ChangeEvent<{}>, newValue: number) => {
|
12
|
+
setValue(newValue)
|
13
|
+
}
|
14
|
+
|
15
|
+
return (
|
16
|
+
<div style={{ width: '13rem' }}>
|
17
|
+
<Tabs value={value} onChange={handleChange}>
|
18
|
+
{Array.from({ length: TAB_COUNT }).map((_, index) => (
|
19
|
+
<Tabs.Tab key={index} label='Label' />
|
20
|
+
))}
|
21
|
+
</Tabs>
|
22
|
+
<Container top={SPACING_4}>Content of tab #{value}</Container>
|
23
|
+
</div>
|
24
|
+
)
|
25
|
+
}
|
26
|
+
|
27
|
+
export default Example
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import { Container, Tabs } from '@toptal/picasso'
|
3
|
+
import { SPACING_4, shadows, sizes, palette } from '@toptal/picasso-utils'
|
4
|
+
|
5
|
+
type TabPanelProps = {
|
6
|
+
children: React.ReactNode
|
7
|
+
value: number
|
8
|
+
index: number
|
9
|
+
}
|
10
|
+
const TabPanel = ({ children, value, index }: TabPanelProps) => (
|
11
|
+
<Container role='tabpanel' hidden={value !== index}>
|
12
|
+
{children}
|
13
|
+
</Container>
|
14
|
+
)
|
15
|
+
|
16
|
+
// This component is not from BASE, just an example how it can be used
|
17
|
+
const TabsContent = ({ children }: { children: React.ReactNode }) => {
|
18
|
+
return (
|
19
|
+
<Container
|
20
|
+
style={{
|
21
|
+
flex: '1 1 auto',
|
22
|
+
backgroundColor: palette.grey.lightest,
|
23
|
+
boxShadow: shadows[1],
|
24
|
+
borderRadius: sizes.borderRadius.medium,
|
25
|
+
}}
|
26
|
+
padded={SPACING_4}
|
27
|
+
>
|
28
|
+
{children}
|
29
|
+
</Container>
|
30
|
+
)
|
31
|
+
}
|
32
|
+
|
33
|
+
const Example = () => {
|
34
|
+
const [value, setValue] = React.useState(0)
|
35
|
+
|
36
|
+
const handleChange = (_: React.ChangeEvent<{}>, newValue: number) => {
|
37
|
+
setValue(newValue)
|
38
|
+
}
|
39
|
+
|
40
|
+
return (
|
41
|
+
<Container
|
42
|
+
style={{
|
43
|
+
backgroundColor: palette.grey.lighter,
|
44
|
+
}}
|
45
|
+
padded={SPACING_4}
|
46
|
+
flex
|
47
|
+
>
|
48
|
+
<Tabs onChange={handleChange} orientation='vertical' value={value}>
|
49
|
+
<Tabs.Tab
|
50
|
+
avatar='./jacqueline-with-flowers-1954-square.jpg'
|
51
|
+
description='UI specialist'
|
52
|
+
label='Jacqueline Roque'
|
53
|
+
/>
|
54
|
+
<Tabs.Tab
|
55
|
+
avatar='./jacqueline-with-flowers-1954-square.jpg'
|
56
|
+
description='UI specialist'
|
57
|
+
label='Jacqueline Roque'
|
58
|
+
/>
|
59
|
+
<Tabs.Tab
|
60
|
+
avatar=''
|
61
|
+
description='UI specialist'
|
62
|
+
label='Jacqueline Roque'
|
63
|
+
/>
|
64
|
+
<Tabs.Tab avatar={null} description='UI specialist' label='John Doe' />
|
65
|
+
</Tabs>
|
66
|
+
<TabsContent>
|
67
|
+
<TabPanel value={value} index={0}>
|
68
|
+
Content of the first tab
|
69
|
+
</TabPanel>
|
70
|
+
<TabPanel value={value} index={1}>
|
71
|
+
Content of the second tab
|
72
|
+
</TabPanel>
|
73
|
+
<TabPanel value={value} index={2}>
|
74
|
+
Content of the third tab
|
75
|
+
</TabPanel>
|
76
|
+
<TabPanel value={value} index={3}>
|
77
|
+
Content of the third tab
|
78
|
+
</TabPanel>
|
79
|
+
</TabsContent>
|
80
|
+
</Container>
|
81
|
+
)
|
82
|
+
}
|
83
|
+
|
84
|
+
export default Example
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import { Tabs } from '../Tabs'
|
2
|
+
import tabStory from '../../Tab/story'
|
3
|
+
import PicassoBook from '~/.storybook/components/PicassoBook'
|
4
|
+
|
5
|
+
const page = PicassoBook.section('Layout').createPage(
|
6
|
+
'Tabs',
|
7
|
+
`
|
8
|
+
Tabs allow to switch between content sections
|
9
|
+
|
10
|
+
${PicassoBook.createBaseDocsLink(
|
11
|
+
'https://www.figma.com/file/5SCTOPrCDcHuk5We091GBn/Product-Library?node-id=246%3A11213'
|
12
|
+
)}
|
13
|
+
|
14
|
+
${PicassoBook.createSourceLink(__filename)}
|
15
|
+
`
|
16
|
+
)
|
17
|
+
|
18
|
+
page
|
19
|
+
.createTabChapter('Props')
|
20
|
+
.addComponentDocs({ component: Tabs, name: 'Tabs' })
|
21
|
+
.addComponentDocs(tabStory.componentDocs)
|
22
|
+
|
23
|
+
page
|
24
|
+
.createChapter()
|
25
|
+
.addExample(
|
26
|
+
'Tabs/story/Default.example.tsx',
|
27
|
+
{
|
28
|
+
title: 'Default',
|
29
|
+
takeScreenshot: false,
|
30
|
+
},
|
31
|
+
'base/Tabs'
|
32
|
+
)
|
33
|
+
.addExample(
|
34
|
+
'Tabs/story/Vertical.example.tsx',
|
35
|
+
{
|
36
|
+
title: 'Vertical',
|
37
|
+
takeScreenshot: false,
|
38
|
+
description: '⚠️ Not responsive',
|
39
|
+
},
|
40
|
+
'base/Tabs'
|
41
|
+
)
|
42
|
+
.addExample(
|
43
|
+
'Tabs/story/ScrollButtons.example.tsx',
|
44
|
+
{
|
45
|
+
title: 'Scroll buttons',
|
46
|
+
takeScreenshot: false,
|
47
|
+
},
|
48
|
+
'base/Tabs'
|
49
|
+
)
|
50
|
+
.addExample(
|
51
|
+
'Tabs/story/FullWidth.example.tsx',
|
52
|
+
{
|
53
|
+
title: 'Full Width',
|
54
|
+
screenshotBreakpoints: true,
|
55
|
+
},
|
56
|
+
'base/Tabs'
|
57
|
+
)
|
58
|
+
page.connect(tabStory.chapter)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import type { Theme } from '@material-ui/core/styles'
|
2
|
+
import { createStyles } from '@material-ui/core/styles'
|
3
|
+
import { PicassoProvider } from '@toptal/picasso-provider'
|
4
|
+
|
5
|
+
PicassoProvider.override(({ palette }: Theme) => ({
|
6
|
+
MuiTabs: {
|
7
|
+
root: {
|
8
|
+
position: 'relative',
|
9
|
+
minHeight: 0,
|
10
|
+
},
|
11
|
+
vertical: {
|
12
|
+
width: 200,
|
13
|
+
margin: 0,
|
14
|
+
'& $scroller': {
|
15
|
+
// We need a bit of padding to allow active tab's shadow to be visible
|
16
|
+
paddingLeft: '0.5em',
|
17
|
+
},
|
18
|
+
|
19
|
+
'& $indicator': {
|
20
|
+
display: 'none',
|
21
|
+
},
|
22
|
+
},
|
23
|
+
indicator: {
|
24
|
+
backgroundColor: palette.blue.main,
|
25
|
+
zIndex: 1,
|
26
|
+
},
|
27
|
+
},
|
28
|
+
}))
|
29
|
+
|
30
|
+
export default ({ palette }: Theme) =>
|
31
|
+
createStyles({
|
32
|
+
horizontal: {
|
33
|
+
'&::after': {
|
34
|
+
position: 'absolute',
|
35
|
+
content: '""',
|
36
|
+
bottom: 0,
|
37
|
+
left: 0,
|
38
|
+
right: 0,
|
39
|
+
height: 1,
|
40
|
+
backgroundColor: palette.grey.main,
|
41
|
+
zIndex: 0,
|
42
|
+
},
|
43
|
+
},
|
44
|
+
vertical: {},
|
45
|
+
})
|
@@ -0,0 +1,165 @@
|
|
1
|
+
/* eslint-disable react/no-array-index-key */
|
2
|
+
import React from 'react'
|
3
|
+
import { render, fireEvent } from '@testing-library/react'
|
4
|
+
import type { OmitInternalProps } from '@toptal/picasso-shared'
|
5
|
+
import { TestingPicasso } from '@toptal/picasso-test-utils'
|
6
|
+
|
7
|
+
import type { TabProps } from '../Tab'
|
8
|
+
import type { Props } from './Tabs'
|
9
|
+
import { TabsCompound as Tabs } from '../TabsCompound'
|
10
|
+
|
11
|
+
const renderTabContent = (tab: TabProps, index: number, value: any) => {
|
12
|
+
const isTabActive = index + 1 === value || tab.value === value
|
13
|
+
const testId = `tab-${index + 1}-content`
|
14
|
+
|
15
|
+
if (isTabActive) {
|
16
|
+
return (
|
17
|
+
<div key={testId} data-testid={testId}>
|
18
|
+
Tab #{index + 1} content
|
19
|
+
</div>
|
20
|
+
)
|
21
|
+
}
|
22
|
+
|
23
|
+
return null
|
24
|
+
}
|
25
|
+
|
26
|
+
const renderTabs = (
|
27
|
+
tabs: TabProps[],
|
28
|
+
{ value, onChange, variant }: OmitInternalProps<Props, 'children'>,
|
29
|
+
orientation: 'horizontal' | 'vertical' = 'horizontal'
|
30
|
+
) => {
|
31
|
+
return render(
|
32
|
+
<TestingPicasso>
|
33
|
+
<Tabs
|
34
|
+
onChange={onChange}
|
35
|
+
value={value}
|
36
|
+
orientation={orientation}
|
37
|
+
variant={variant}
|
38
|
+
>
|
39
|
+
{tabs.map((tab, index) => (
|
40
|
+
<Tabs.Tab
|
41
|
+
key={index}
|
42
|
+
data-testid={`tab-${index + 1}`}
|
43
|
+
value={tab.value}
|
44
|
+
label={tab.label}
|
45
|
+
disabled={tab.disabled}
|
46
|
+
/>
|
47
|
+
))}
|
48
|
+
</Tabs>
|
49
|
+
|
50
|
+
{tabs.map((tab, index) => renderTabContent(tab, index, value))}
|
51
|
+
</TestingPicasso>
|
52
|
+
)
|
53
|
+
}
|
54
|
+
|
55
|
+
describe('Tabs', () => {
|
56
|
+
it('renders', () => {
|
57
|
+
const { container, queryByTestId } = renderTabs(
|
58
|
+
[{ label: 'Tab 1' }, { label: 'Tab 2' }],
|
59
|
+
{
|
60
|
+
value: false,
|
61
|
+
}
|
62
|
+
)
|
63
|
+
|
64
|
+
expect(queryByTestId('tab-1-content')).not.toBeInTheDocument()
|
65
|
+
expect(queryByTestId('tab-2-content')).not.toBeInTheDocument()
|
66
|
+
|
67
|
+
expect(container).toMatchSnapshot()
|
68
|
+
})
|
69
|
+
|
70
|
+
it('renders in vertical orientation', () => {
|
71
|
+
const { container } = renderTabs(
|
72
|
+
[{ label: 'Tab 1' }, { label: 'Tab 2' }],
|
73
|
+
{ value: false },
|
74
|
+
'vertical'
|
75
|
+
)
|
76
|
+
|
77
|
+
expect(container).toMatchSnapshot()
|
78
|
+
})
|
79
|
+
|
80
|
+
it('renders with a pre-selected option', () => {
|
81
|
+
const { container, queryByTestId } = renderTabs(
|
82
|
+
[{ label: 'Tab 1' }, { label: 'Tab 2' }],
|
83
|
+
{
|
84
|
+
value: 1,
|
85
|
+
}
|
86
|
+
)
|
87
|
+
|
88
|
+
expect(queryByTestId('tab-2-content')).not.toBeInTheDocument()
|
89
|
+
expect(queryByTestId('tab-1-content')).toBeInTheDocument()
|
90
|
+
|
91
|
+
expect(container).toMatchSnapshot()
|
92
|
+
})
|
93
|
+
|
94
|
+
it('renders with a pre-selected option using custom value', () => {
|
95
|
+
const { container, queryByTestId } = renderTabs(
|
96
|
+
[
|
97
|
+
{ label: 'Tab 1', value: 'tab-1' },
|
98
|
+
{ label: 'Tab 2', value: 'tab-2' },
|
99
|
+
],
|
100
|
+
{
|
101
|
+
value: 'tab-1',
|
102
|
+
}
|
103
|
+
)
|
104
|
+
|
105
|
+
expect(queryByTestId('tab-2-content')).not.toBeInTheDocument()
|
106
|
+
expect(queryByTestId('tab-1-content')).toBeInTheDocument()
|
107
|
+
|
108
|
+
expect(container).toMatchSnapshot()
|
109
|
+
})
|
110
|
+
|
111
|
+
it('fires onChange when clicked', () => {
|
112
|
+
const onChange = jest.fn()
|
113
|
+
const { getByTestId } = renderTabs(
|
114
|
+
[{ label: 'Tab 1' }, { label: 'Tab 2' }],
|
115
|
+
{
|
116
|
+
value: 1,
|
117
|
+
onChange,
|
118
|
+
}
|
119
|
+
)
|
120
|
+
|
121
|
+
fireEvent.click(getByTestId('tab-2'))
|
122
|
+
expect(onChange).toHaveBeenCalledTimes(1)
|
123
|
+
})
|
124
|
+
|
125
|
+
it('fires onChange with custom value when clicked', () => {
|
126
|
+
const onChange = jest.fn()
|
127
|
+
const { getByTestId } = renderTabs(
|
128
|
+
[
|
129
|
+
{ label: 'Tab 1', value: 'first' },
|
130
|
+
{ label: 'Tab 2', value: 'second' },
|
131
|
+
],
|
132
|
+
{
|
133
|
+
value: 'first',
|
134
|
+
onChange,
|
135
|
+
}
|
136
|
+
)
|
137
|
+
|
138
|
+
fireEvent.click(getByTestId('tab-2'))
|
139
|
+
expect(onChange).toHaveBeenCalledTimes(1)
|
140
|
+
expect(onChange).toHaveBeenCalledWith(expect.anything(), 'second')
|
141
|
+
})
|
142
|
+
|
143
|
+
it('doesnt fire onChange when disabled', () => {
|
144
|
+
const onChange = jest.fn()
|
145
|
+
const { getByTestId } = renderTabs(
|
146
|
+
[{ label: 'Tab 1' }, { label: 'Tab 2', disabled: true }],
|
147
|
+
{
|
148
|
+
value: 1,
|
149
|
+
onChange,
|
150
|
+
}
|
151
|
+
)
|
152
|
+
|
153
|
+
fireEvent.click(getByTestId('tab-2'))
|
154
|
+
expect(onChange).toHaveBeenCalledTimes(0)
|
155
|
+
})
|
156
|
+
|
157
|
+
it('renders in full width', () => {
|
158
|
+
const { container } = renderTabs([{ label: 'Tab 1' }, { label: 'Tab 2' }], {
|
159
|
+
value: false,
|
160
|
+
variant: 'fullWidth',
|
161
|
+
})
|
162
|
+
|
163
|
+
expect(container).toMatchSnapshot()
|
164
|
+
})
|
165
|
+
})
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import type { Ref } from 'react'
|
2
|
+
import { useEffect, useRef } from 'react'
|
3
|
+
import type { TabsActions } from '@material-ui/core'
|
4
|
+
|
5
|
+
/*
|
6
|
+
* MuiTabs break when the size of a tab changes without rendering with React.
|
7
|
+
* This issue happens when the font is loaded after the initial render of the component.
|
8
|
+
* To solve this issue, we imperatively update the indicator and scroll buttons when it happens.
|
9
|
+
*/
|
10
|
+
const useTabAction = (): Ref<TabsActions> => {
|
11
|
+
const ref = useRef<TabsActions>(null)
|
12
|
+
|
13
|
+
useEffect(() => {
|
14
|
+
const listener = () => {
|
15
|
+
ref.current?.updateIndicator()
|
16
|
+
ref.current?.updateScrollButtons()
|
17
|
+
}
|
18
|
+
|
19
|
+
window.addEventListener('load', listener)
|
20
|
+
|
21
|
+
return () => window.removeEventListener('load', listener)
|
22
|
+
}, [])
|
23
|
+
|
24
|
+
return ref
|
25
|
+
}
|
26
|
+
|
27
|
+
export default useTabAction
|
package/src/index.ts
ADDED
package/tsconfig.json
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"extends": "../../../tsconfig.base.json",
|
3
|
+
"compilerOptions": { "outDir": "dist-package" },
|
4
|
+
"include": ["src"],
|
5
|
+
"references": [
|
6
|
+
{ "path": "../../picasso-provider" },
|
7
|
+
{ "path": "../../shared" },
|
8
|
+
{ "path": "../Container" },
|
9
|
+
{ "path": "../Icons" },
|
10
|
+
{ "path": "../Typography" },
|
11
|
+
{ "path": "../TypographyOverflow" },
|
12
|
+
{ "path": "../UserBadge" },
|
13
|
+
{ "path": "../Utils" }
|
14
|
+
]
|
15
|
+
}
|