@edvisor/product-language 0.2.0 → 0.3.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/.eslintrc.json +14 -6
- package/package.json +2 -3
- package/project.json +18 -7
- package/src/README.md +61 -0
- package/src/helpers/index.ts +2 -1
- package/src/helpers/playground.ts +16 -0
- package/src/lib/components/badge/badge.tsx +8 -17
- package/src/lib/components/badge/stories/badge.stories.mdx +44 -0
- package/src/lib/components/badge/stories/components.tsx +49 -0
- package/src/lib/components/card/card.test.tsx +4 -5
- package/src/lib/components/card/card.tsx +2 -2
- package/src/lib/components/card/components/card-section-slot.tsx +2 -2
- package/src/lib/components/card/storybook/card.stories.mdx +1 -1
- package/src/lib/components/card/storybook/components.tsx +6 -23
- package/src/lib/components/checkbox/checkbox.tsx +34 -95
- package/src/lib/components/checkbox/helpers.tsx +100 -0
- package/src/lib/components/checkbox/stories/checkbox.stories.mdx +27 -24
- package/src/lib/components/checkbox/stories/components.tsx +63 -15
- package/src/lib/components/divider/stories/divider.stories.mdx +7 -13
- package/src/lib/components/flag/flag-size-flags.tsx +55 -0
- package/src/lib/components/flag/flag.list.tsx +788 -0
- package/src/lib/components/flag/flag.test.tsx +65 -0
- package/src/lib/components/flag/flag.tsx +97 -0
- package/src/lib/components/flag/index.tsx +1 -0
- package/src/lib/components/flag/stories/components.tsx +403 -0
- package/src/lib/components/flag/stories/flag.stories.mdx +48 -0
- package/src/lib/components/flag/stories/playGround-select.tsx +145 -0
- package/src/lib/components/icon/icon-list.tsx +135 -0
- package/src/lib/components/icon/icon.test.tsx +48 -0
- package/src/lib/components/icon/icon.tsx +181 -0
- package/src/lib/components/icon/index.tsx +1 -0
- package/src/lib/components/icon/stories/components.tsx +282 -0
- package/src/lib/components/icon/stories/icon.stories.mdx +65 -0
- package/src/lib/components/index.ts +5 -0
- package/src/lib/components/input-field/components/labeled-input.tsx +7 -14
- package/src/lib/components/input-field/components/stepper.tsx +4 -3
- package/src/lib/components/input-field/input-field.test.tsx +5 -6
- package/src/lib/components/input-field/input-field.tsx +8 -8
- package/src/lib/components/input-field/storybook/components.tsx +9 -16
- package/src/lib/components/link/storybook/link.stories.mdx +1 -0
- package/src/lib/components/molecules/avatar/avatar-size-flags.tsx +3 -3
- package/src/lib/components/molecules/avatar/avatar.tsx +2 -2
- package/src/lib/components/molecules/avatar/stories/avatar.stories.mdx +18 -21
- package/src/lib/components/molecules/button/button-flags.tsx +120 -15
- package/src/lib/components/molecules/button/button.test.tsx +9 -9
- package/src/lib/components/molecules/button/button.tsx +61 -78
- package/src/lib/components/molecules/button/stories/button.stories.mdx +43 -42
- package/src/lib/components/molecules/button/stories/components.tsx +6 -8
- package/src/lib/components/molecules/input-checkbox/input-checkbox.tsx +23 -24
- package/src/lib/components/molecules/input-checkbox/stories/components.tsx +32 -15
- package/src/lib/components/molecules/input-checkbox/stories/input-checkbox.stories.mdx +6 -8
- package/src/lib/components/organisms/multi-choice-list/multi-choice-list.tsx +7 -8
- package/src/lib/components/organisms/multi-choice-list/stories/components.tsx +3 -5
- package/src/lib/components/organisms/multi-choice-list/stories/multi-choice-list.stories.mdx +4 -4
- package/src/lib/components/spinner/spinner.test.tsx +2 -2
- package/src/lib/components/spinner/spinner.tsx +15 -28
- package/src/lib/components/spinner/stories/components.tsx +33 -2
- package/src/lib/components/spinner/stories/spinner.stories.mdx +3 -10
- package/src/lib/components/tabs/components/index.ts +1 -0
- package/src/lib/components/tabs/components/tab.tsx +62 -0
- package/src/lib/components/tabs/index.tsx +1 -0
- package/src/lib/components/tabs/storybook/components.tsx +282 -0
- package/src/lib/components/tabs/storybook/tabs.stories.mdx +97 -0
- package/src/lib/components/tabs/tabs.test.tsx +86 -0
- package/src/lib/components/tabs/tabs.tsx +101 -0
- package/src/lib/components/tag/components/close-button.tsx +85 -0
- package/src/lib/components/tag/components/index.ts +2 -0
- package/src/lib/components/tag/components/tag-label.tsx +44 -0
- package/src/lib/components/tag/index.tsx +1 -0
- package/src/lib/components/tag/stories/components.tsx +86 -0
- package/src/lib/components/tag/stories/tag.stories.mdx +42 -0
- package/src/lib/components/tag/tag.test.tsx +36 -0
- package/src/lib/components/tag/tag.tsx +33 -0
- package/src/lib/components/thumbnail/thumbnail.tsx +7 -2
- package/src/lib/components/typography/storybook/components.tsx +47 -15
- package/src/lib/components/typography/storybook/typography.stories.mdx +6 -4
- package/src/lib/components/typography/typography.test.tsx +34 -30
- package/src/lib/components/typography/typography.tsx +61 -19
- package/src/lib/foundations/color-system/base-palette/base-palette.ts +0 -1
- package/src/lib/foundations/color-system/color-guidelines/color-guidelines.ts +5 -4
- package/src/lib/foundations/color-system/components/color-sample.tsx +3 -3
- package/src/lib/foundations/typography/fonts.ts +205 -0
- package/src/lib/foundations/typography/text-aspect-flags.ts +11 -4
- package/src/lib/foundations/typography/typography.tsx +38 -33
- package/src/lib/helpers/numbers.ts +14 -0
- package/src/lib/helpers/safe-navigation.ts +10 -0
- package/src/lib/helpers/slots.test.tsx +98 -0
- package/src/lib/helpers/slots.tsx +93 -12
- package/.storybook/preview-head.html +0 -1
- package/src/lib/components/badge/badge.stories.tsx +0 -16
- package/src/lib/components/checkbox/components/components.tsx +0 -59
- package/src/lib/components/checkbox/stories/index.tsx +0 -1
- package/src/lib/components/molecules/input-checkbox/stories/index.tsx +0 -1
- package/src/lib/components/organisms/multi-choice-list/stories/index.tsx +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Body, Heading4 } from '@components'
|
|
2
|
+
import { isDefined } from '@helpers'
|
|
2
3
|
import { Playground } from 'storybook-addon-jarle-monaco'
|
|
3
4
|
import { MultiChoiceList } from '../index'
|
|
4
5
|
|
|
@@ -10,7 +11,6 @@ const [withError, setWithError] = useState(false);
|
|
|
10
11
|
|
|
11
12
|
<form onSubmit={(e) => {
|
|
12
13
|
e.preventDefault()
|
|
13
|
-
console.log('form submitted')
|
|
14
14
|
}}
|
|
15
15
|
>
|
|
16
16
|
<MultiChoiceList options={[
|
|
@@ -24,14 +24,12 @@ const [withError, setWithError] = useState(false);
|
|
|
24
24
|
setWithError(true);
|
|
25
25
|
},
|
|
26
26
|
onChange: function(e){
|
|
27
|
-
console.log('at stories', e);
|
|
28
27
|
setWithError(false);
|
|
29
28
|
}
|
|
30
29
|
},
|
|
31
30
|
{
|
|
32
31
|
label: "My Awesome label",
|
|
33
32
|
id: 'My checkbox 2',
|
|
34
|
-
checked: true,
|
|
35
33
|
helpfulMessage: 'Alternative text here',
|
|
36
34
|
},
|
|
37
35
|
]}
|
|
@@ -76,7 +74,7 @@ const [withError, setWithError] = useState(false);
|
|
|
76
74
|
const options = Locations.map((location) => {
|
|
77
75
|
return {
|
|
78
76
|
...location,
|
|
79
|
-
checked: locations.find((l) => l.id === location.id),
|
|
77
|
+
checked: isDefined(locations.find((l) => l.id === location.id)),
|
|
80
78
|
onChange: (checked) => {
|
|
81
79
|
if (checked) {
|
|
82
80
|
setLocations([
|
|
@@ -97,7 +95,6 @@ const options = Locations.map((location) => {
|
|
|
97
95
|
<>
|
|
98
96
|
<form onSubmit={(e) => {
|
|
99
97
|
e.preventDefault()
|
|
100
|
-
console.log('locations', locations)
|
|
101
98
|
}}
|
|
102
99
|
>
|
|
103
100
|
<MultiChoiceList options={options}
|
|
@@ -120,6 +117,7 @@ const options = Locations.map((location) => {
|
|
|
120
117
|
MultiChoiceList,
|
|
121
118
|
Heading4,
|
|
122
119
|
Body,
|
|
120
|
+
isDefined,
|
|
123
121
|
},
|
|
124
122
|
}}
|
|
125
123
|
/>
|
package/src/lib/components/organisms/multi-choice-list/stories/multi-choice-list.stories.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
2
|
-
import { MultiChoiceList } from '../
|
|
3
|
-
import { PlainHTMLFormExample, ExampleWithStateManagement } from './
|
|
2
|
+
import { MultiChoiceList } from '../multi-choice-list'
|
|
3
|
+
import { PlainHTMLFormExample, ExampleWithStateManagement } from './components'
|
|
4
4
|
|
|
5
5
|
<Meta
|
|
6
6
|
title="Components/MultiChoiceList"
|
|
@@ -35,7 +35,7 @@ import { MultiChoiceList } from './index'
|
|
|
35
35
|
<MultiChoiceList options={[
|
|
36
36
|
{
|
|
37
37
|
label: "My Awesome label",
|
|
38
|
-
|
|
38
|
+
defaultChecked: true,
|
|
39
39
|
id: '1',
|
|
40
40
|
},
|
|
41
41
|
{
|
|
@@ -59,7 +59,7 @@ Also, you can send the altive text in order to helping user
|
|
|
59
59
|
{
|
|
60
60
|
label: "My Awesome label",
|
|
61
61
|
id: 'My checkbox 1',
|
|
62
|
-
|
|
62
|
+
defaultChecked: true,
|
|
63
63
|
error: true,
|
|
64
64
|
helpfulMessage: 'Alternative text here',
|
|
65
65
|
},
|
|
@@ -8,7 +8,7 @@ describe('Spinner Tests', () => {
|
|
|
8
8
|
<Spinner />
|
|
9
9
|
)
|
|
10
10
|
|
|
11
|
-
const spinner = screen.getByRole('
|
|
11
|
+
const spinner = screen.getByRole('icon')
|
|
12
12
|
|
|
13
13
|
expect(spinner).toBeInTheDocument()
|
|
14
14
|
expect(spinner).toHaveStyle('height: 40px')
|
|
@@ -20,7 +20,7 @@ describe('Spinner Tests', () => {
|
|
|
20
20
|
<Spinner small/>
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
-
const spinner = screen.getByRole('
|
|
23
|
+
const spinner = screen.getByRole('icon')
|
|
24
24
|
|
|
25
25
|
expect(spinner).toBeInTheDocument()
|
|
26
26
|
expect(spinner).toHaveStyle('height: 18px')
|
|
@@ -2,6 +2,7 @@ import { getValuesBySize, SpinnerProps } from './spinner-size-flags'
|
|
|
2
2
|
import { FC, is } from '@helpers'
|
|
3
3
|
import styled, { keyframes } from 'styled-components'
|
|
4
4
|
import { Icons } from '@foundations'
|
|
5
|
+
import { IconMinor } from 'components/icon'
|
|
5
6
|
|
|
6
7
|
const spinAnimation = keyframes`
|
|
7
8
|
0% {
|
|
@@ -18,26 +19,15 @@ const spinAnimation = keyframes`
|
|
|
18
19
|
}
|
|
19
20
|
`
|
|
20
21
|
|
|
21
|
-
const Spin = styled.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-webkit-transform-origin: 20px 20 0;
|
|
31
|
-
-moz-transform-origin: 20px 20px 0;
|
|
32
|
-
-ms-transform-origin: 20px 20px 0;
|
|
33
|
-
-o-transform-origin: 20px 20px 0;
|
|
34
|
-
transform-origin: 20px 20px 0;
|
|
35
|
-
-webkit-animation: spinner 2s linear infinite;
|
|
36
|
-
-moz-animation: spinner 2s linear infinite;
|
|
37
|
-
-ms-animation: spinner 2s linear infinite;
|
|
38
|
-
-o-animation: spinner 2s linear infinite;
|
|
39
|
-
animation: spinner 2s linear infinite;
|
|
40
|
-
animation-name: ${spinAnimation};
|
|
22
|
+
const Spin = styled.div<{ $size: string, $onPrimary: boolean, $onCritical: boolean }>`
|
|
23
|
+
svg {
|
|
24
|
+
width: ${({ $size }) => $size};
|
|
25
|
+
height: ${({ $size }) => $size};
|
|
26
|
+
position: unset;
|
|
27
|
+
path {
|
|
28
|
+
fill: ${({ $onPrimary, $onCritical }) => ($onPrimary || $onCritical) ? Icons.OnPrimary : Icons.Default};
|
|
29
|
+
}
|
|
30
|
+
animation: ${spinAnimation} infinite 2s linear;
|
|
41
31
|
}
|
|
42
32
|
`
|
|
43
33
|
|
|
@@ -52,15 +42,12 @@ export const Spinner : FC<IProps> = (props) => {
|
|
|
52
42
|
|
|
53
43
|
return (
|
|
54
44
|
<Spin
|
|
55
|
-
onPrimary={is(props.onPrimary)}
|
|
56
|
-
onCritical={is(props.onCritical)}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
role="progressbar"
|
|
60
|
-
viewBox="0 0 40 40"
|
|
61
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
45
|
+
$onPrimary={is(props.onPrimary)}
|
|
46
|
+
$onCritical={is(props.onCritical)}
|
|
47
|
+
$size={size}
|
|
48
|
+
className={props.className}
|
|
62
49
|
>
|
|
63
|
-
<
|
|
50
|
+
<IconMinor.Spinner />
|
|
64
51
|
</Spin>
|
|
65
52
|
)
|
|
66
53
|
}
|
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
import styled from 'styled-components'
|
|
2
2
|
import { Padding, Surface } from '@foundations'
|
|
3
|
+
import { Playground } from 'storybook-addon-jarle-monaco'
|
|
4
|
+
import { Button } from 'components/molecules'
|
|
5
|
+
import { Spinner } from '../spinner'
|
|
3
6
|
|
|
4
|
-
export const
|
|
5
|
-
width: 100%;
|
|
7
|
+
export const Div = styled.div`
|
|
6
8
|
padding: ${Padding.s};
|
|
7
9
|
background: ${Surface.Default.Default};
|
|
10
|
+
display: flex;
|
|
11
|
+
gap: 20px;
|
|
8
12
|
`
|
|
13
|
+
|
|
14
|
+
export const SpinnerCodeExample = () => (
|
|
15
|
+
<Playground
|
|
16
|
+
code={`
|
|
17
|
+
// Try replacing the Button types.
|
|
18
|
+
<Div>
|
|
19
|
+
<Button primary>
|
|
20
|
+
<Spinner onPrimary />
|
|
21
|
+
</Button>
|
|
22
|
+
<Button destructive primary>
|
|
23
|
+
<Spinner onCritical />
|
|
24
|
+
</Button>
|
|
25
|
+
<Button destructive>
|
|
26
|
+
<Spinner onCritical />
|
|
27
|
+
</Button>
|
|
28
|
+
</Div>
|
|
29
|
+
`}
|
|
30
|
+
providerProps={{
|
|
31
|
+
renderAsComponent: true,
|
|
32
|
+
scope: {
|
|
33
|
+
Button,
|
|
34
|
+
Spinner,
|
|
35
|
+
Div,
|
|
36
|
+
},
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
|
|
2
2
|
import { StoryComponent, talesOf, StorybookFrame } from '@stories'
|
|
3
3
|
import { Spinner } from '../index'
|
|
4
|
-
import {
|
|
4
|
+
import { Button } from 'components/molecules/button'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { SpinnerCodeExample } from './components'
|
|
7
7
|
|
|
8
8
|
<Meta title="Components/Spinner" />
|
|
9
9
|
|
|
@@ -32,11 +32,4 @@ import { Spinner } from './index'
|
|
|
32
32
|
|
|
33
33
|
When rendering the spinner on a primary or critical surface, use onPrimary or onCritical.
|
|
34
34
|
|
|
35
|
-
<
|
|
36
|
-
<ButtonPrimary>
|
|
37
|
-
<Spinner onPrimary />
|
|
38
|
-
</ButtonPrimary>
|
|
39
|
-
<ButtonDestructive primary>
|
|
40
|
-
<Spinner onCritical />
|
|
41
|
-
</ButtonDestructive>
|
|
42
|
-
</Canvas>
|
|
35
|
+
<SpinnerCodeExample/>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tab'
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CSSProperties, HTMLAttributes, useEffect, useRef } from 'react'
|
|
2
|
+
import { FC, isDefined, Nothing, PropsWithChildren } from '@helpers'
|
|
3
|
+
import { Borders, Margin, Padding } from '@foundations'
|
|
4
|
+
import { Label } from 'components/typography'
|
|
5
|
+
|
|
6
|
+
const styles: (props: { selected: boolean }) => CSSProperties = ({ selected }) => ({
|
|
7
|
+
display: 'flex',
|
|
8
|
+
padding: Padding.m,
|
|
9
|
+
cursor: 'pointer',
|
|
10
|
+
outline: 'none',
|
|
11
|
+
borderBottom: selected ? `3px solid ${Borders.Highlight.Default}` : '',
|
|
12
|
+
':focus': {
|
|
13
|
+
borderBottom: `3px solid ${Borders.Highlight.Default}`
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export interface ITabProps extends PropsWithChildren, HTMLAttributes<HTMLDivElement> {
|
|
18
|
+
Prefix?: FC<HTMLAttributes<HTMLElement>>
|
|
19
|
+
selected?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const Tab: FC<ITabProps> = (props) => {
|
|
23
|
+
const {
|
|
24
|
+
Prefix,
|
|
25
|
+
selected = false,
|
|
26
|
+
...rest
|
|
27
|
+
} = props
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line no-null/no-null
|
|
30
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
selected ? ref.current?.focus() : ref.current?.blur()
|
|
34
|
+
}, [selected])
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @TODO this is a hack to be able to use the ref in react-web-client
|
|
38
|
+
* RWC uses styled-components < v4 and expects the ref to be passes as
|
|
39
|
+
* "innerRef" not just "ref", but product-language uses > v4 and wants
|
|
40
|
+
* to use ref. So for some components that need a ref, like this one
|
|
41
|
+
* we will have to use a div and style it the old fashioned way until
|
|
42
|
+
* we upgrade rwc's styled-components or drop rwc
|
|
43
|
+
*/
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
{...rest}
|
|
47
|
+
tabIndex={0}
|
|
48
|
+
onClick={() => {
|
|
49
|
+
ref.current?.focus()
|
|
50
|
+
}}
|
|
51
|
+
role={'tab'}
|
|
52
|
+
aria-selected={selected}
|
|
53
|
+
ref={ref}
|
|
54
|
+
style={styles({ selected })}
|
|
55
|
+
>
|
|
56
|
+
{isDefined(Prefix) ? <Prefix style={{ marginRight: Margin.xs}} /> : <Nothing />}
|
|
57
|
+
<Label strong subdued={!selected}>
|
|
58
|
+
{props.children}
|
|
59
|
+
</Label>
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tabs'
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import styled from 'styled-components'
|
|
2
|
+
|
|
3
|
+
import { Padding, Margin } from '@foundations'
|
|
4
|
+
import { CardPlayground, Playground } from '@stories'
|
|
5
|
+
|
|
6
|
+
import { CardFrame } from 'components/card/atoms'
|
|
7
|
+
import { Tab, Tabs } from '../tabs'
|
|
8
|
+
import { InputField as InputFieldBase } from 'components/input-field'
|
|
9
|
+
import { Button } from 'components/molecules'
|
|
10
|
+
import { Flex } from 'components/layout'
|
|
11
|
+
import { Label } from 'components/typography'
|
|
12
|
+
import { FC, Nothing } from '@helpers'
|
|
13
|
+
import { Card } from 'components/card'
|
|
14
|
+
import { IconMinor as Icon } from 'components/icon'
|
|
15
|
+
|
|
16
|
+
const ContentFrame = styled(Tabs.Content)`
|
|
17
|
+
padding: ${Padding.l};
|
|
18
|
+
`
|
|
19
|
+
|
|
20
|
+
export const AdditionalControlsExample = () => (
|
|
21
|
+
<Playground
|
|
22
|
+
code={`
|
|
23
|
+
const [selected, setSelected] = useState(0);
|
|
24
|
+
|
|
25
|
+
<Tabs selected={selected} onChange={setSelected}>
|
|
26
|
+
<Tabs.TabList>
|
|
27
|
+
<div>This is not a tab.</div>
|
|
28
|
+
<Tab>No Icon</Tab>
|
|
29
|
+
<Tab Prefix={Icon.Comment}>With Icon</Tab>
|
|
30
|
+
<div>This is also not a tab.</div>
|
|
31
|
+
</Tabs.TabList>
|
|
32
|
+
<ContentFrame>
|
|
33
|
+
One
|
|
34
|
+
</ContentFrame>
|
|
35
|
+
<ContentFrame>
|
|
36
|
+
Two
|
|
37
|
+
</ContentFrame>
|
|
38
|
+
</Tabs>
|
|
39
|
+
`}
|
|
40
|
+
providerProps={{
|
|
41
|
+
renderAsComponent: true,
|
|
42
|
+
scope: {
|
|
43
|
+
Tabs,
|
|
44
|
+
Tab,
|
|
45
|
+
ContentFrame,
|
|
46
|
+
Icon,
|
|
47
|
+
Card,
|
|
48
|
+
},
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
export const CrazyTabsExample = () => (
|
|
54
|
+
<Playground
|
|
55
|
+
code={`
|
|
56
|
+
const [selected, setSelected] = useState(0);
|
|
57
|
+
|
|
58
|
+
<Tabs selected={selected} onChange={setSelected}>
|
|
59
|
+
<ContentFrame>
|
|
60
|
+
One
|
|
61
|
+
</ContentFrame>
|
|
62
|
+
<ContentFrame>
|
|
63
|
+
Two
|
|
64
|
+
</ContentFrame>
|
|
65
|
+
<div>This message will render on every tab</div>
|
|
66
|
+
<Tabs.TabList>
|
|
67
|
+
<Tab>No Icon</Tab>
|
|
68
|
+
<Tab Prefix={Icon.Comment}>With Icon</Tab>
|
|
69
|
+
</Tabs.TabList>
|
|
70
|
+
</Tabs>
|
|
71
|
+
`}
|
|
72
|
+
providerProps={{
|
|
73
|
+
renderAsComponent: true,
|
|
74
|
+
scope: {
|
|
75
|
+
Tabs,
|
|
76
|
+
Tab,
|
|
77
|
+
ContentFrame,
|
|
78
|
+
Icon,
|
|
79
|
+
Card,
|
|
80
|
+
},
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
export const TabsWithIconsExample = () => (
|
|
86
|
+
<Playground
|
|
87
|
+
code={`
|
|
88
|
+
const [selected, setSelected] = useState(0);
|
|
89
|
+
|
|
90
|
+
<Tabs selected={selected} onChange={setSelected}>
|
|
91
|
+
<Tabs.TabList>
|
|
92
|
+
<Tab>No Icon</Tab>
|
|
93
|
+
<Tab Prefix={Icon.Comment}>With Icon</Tab>
|
|
94
|
+
</Tabs.TabList>
|
|
95
|
+
<ContentFrame>
|
|
96
|
+
One
|
|
97
|
+
</ContentFrame>
|
|
98
|
+
<ContentFrame>
|
|
99
|
+
Two
|
|
100
|
+
</ContentFrame>
|
|
101
|
+
</Tabs>
|
|
102
|
+
`}
|
|
103
|
+
providerProps={{
|
|
104
|
+
renderAsComponent: true,
|
|
105
|
+
scope: {
|
|
106
|
+
Tabs,
|
|
107
|
+
Tab,
|
|
108
|
+
ContentFrame,
|
|
109
|
+
Icon,
|
|
110
|
+
Card,
|
|
111
|
+
},
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
export const RenderingContentExample = () => (
|
|
117
|
+
<Playground
|
|
118
|
+
code={`
|
|
119
|
+
const [selected, setSelected] = useState(0);
|
|
120
|
+
|
|
121
|
+
<Tabs selected={selected} onChange={setSelected}>
|
|
122
|
+
<Tabs.TabList>
|
|
123
|
+
<Tab Prefix={Icon.Comment}>Style Directly</Tab>
|
|
124
|
+
<Tab Prefix={Icon.GraduationCap}>Use sub-components</Tab>
|
|
125
|
+
</Tabs.TabList>
|
|
126
|
+
<ContentFrame>
|
|
127
|
+
This was styled directly.
|
|
128
|
+
</ContentFrame>
|
|
129
|
+
<Tabs.Content>
|
|
130
|
+
<Card>This is in a Card.</Card>
|
|
131
|
+
</Tabs.Content>
|
|
132
|
+
</Tabs>
|
|
133
|
+
`}
|
|
134
|
+
providerProps={{
|
|
135
|
+
renderAsComponent: true,
|
|
136
|
+
scope: {
|
|
137
|
+
Tabs,
|
|
138
|
+
Tab,
|
|
139
|
+
ContentFrame,
|
|
140
|
+
Icon,
|
|
141
|
+
Card,
|
|
142
|
+
},
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
const InputField = styled(InputFieldBase)`
|
|
148
|
+
margin-right: ${Margin.s};
|
|
149
|
+
flex-grow: 1;
|
|
150
|
+
`
|
|
151
|
+
|
|
152
|
+
const Controls = styled(Flex)`
|
|
153
|
+
width: 100%;
|
|
154
|
+
justify-content: end;
|
|
155
|
+
align-items: center;
|
|
156
|
+
padding-right: ${Padding.l};
|
|
157
|
+
`
|
|
158
|
+
|
|
159
|
+
const NationalityButton = styled(Button)`
|
|
160
|
+
display: flex;
|
|
161
|
+
`
|
|
162
|
+
|
|
163
|
+
const NationalityButtonPrefix: FC = () => {
|
|
164
|
+
return (
|
|
165
|
+
<Flex>
|
|
166
|
+
<Icon.Comment />
|
|
167
|
+
<Icon.GraduationCap />
|
|
168
|
+
</Flex>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const TabsExample = () => (
|
|
173
|
+
<CardPlayground
|
|
174
|
+
code={`
|
|
175
|
+
const [selected, setSelected] = useState(0);
|
|
176
|
+
|
|
177
|
+
<Card>
|
|
178
|
+
<Tabs selected={selected} onChange={setSelected}>
|
|
179
|
+
<Tabs.TabList>
|
|
180
|
+
<Tab Prefix={Icon.Comment}>Language</Tab>
|
|
181
|
+
<Tab Prefix={Icon.GraduationCap}>Higher Ed.</Tab>
|
|
182
|
+
<Controls>
|
|
183
|
+
<NationalityButton
|
|
184
|
+
plain subtle
|
|
185
|
+
IconPrefix={NationalityButtonPrefix}
|
|
186
|
+
>
|
|
187
|
+
Brazil, Onshore
|
|
188
|
+
</NationalityButton>
|
|
189
|
+
</Controls>
|
|
190
|
+
</Tabs.TabList>
|
|
191
|
+
<ContentFrame>
|
|
192
|
+
<Flex center>
|
|
193
|
+
<InputField
|
|
194
|
+
label='Destination'
|
|
195
|
+
placeholder='Search'
|
|
196
|
+
/>
|
|
197
|
+
<InputField
|
|
198
|
+
label='Provider'
|
|
199
|
+
placeholder='Any'
|
|
200
|
+
/>
|
|
201
|
+
<InputField
|
|
202
|
+
label='Min. Number of Weeks'
|
|
203
|
+
placeholder='4'
|
|
204
|
+
/>
|
|
205
|
+
<Button large primary>Search</Button>
|
|
206
|
+
</Flex>
|
|
207
|
+
</ContentFrame>
|
|
208
|
+
<ContentFrame>
|
|
209
|
+
In progress...
|
|
210
|
+
</ContentFrame>
|
|
211
|
+
</Tabs>
|
|
212
|
+
</Card>
|
|
213
|
+
`}
|
|
214
|
+
providerProps={{
|
|
215
|
+
renderAsComponent: true,
|
|
216
|
+
scope: {
|
|
217
|
+
Tabs,
|
|
218
|
+
ContentFrame,
|
|
219
|
+
Tab,
|
|
220
|
+
InputField,
|
|
221
|
+
Flex,
|
|
222
|
+
Controls,
|
|
223
|
+
NationalityButton,
|
|
224
|
+
Label,
|
|
225
|
+
NationalityButtonPrefix,
|
|
226
|
+
Button,
|
|
227
|
+
Card: CardFrame,
|
|
228
|
+
Icon,
|
|
229
|
+
},
|
|
230
|
+
}}
|
|
231
|
+
/>
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
export const StateManagementExample = () => (
|
|
235
|
+
<CardPlayground
|
|
236
|
+
code={`
|
|
237
|
+
const [selected, setSelected] = useState(0);
|
|
238
|
+
|
|
239
|
+
<Card>
|
|
240
|
+
<Tabs selected={selected} onChange={setSelected}>
|
|
241
|
+
<Tabs.TabList>
|
|
242
|
+
<Tab Prefix={Icon.Comment}>One</Tab>
|
|
243
|
+
<Tab Prefix={Icon.GraduationCap}>Two</Tab>
|
|
244
|
+
<Tab Prefix={Icon.Check}>Three</Tab>
|
|
245
|
+
{selected > 0 ? <Button onClick={() => setSelected(0)}>Reset</Button> : <Nothing />}
|
|
246
|
+
</Tabs.TabList>
|
|
247
|
+
<ContentFrame>
|
|
248
|
+
ONE
|
|
249
|
+
<Button onClick={() => setSelected(1)}>Next</Button>
|
|
250
|
+
</ContentFrame>
|
|
251
|
+
<ContentFrame>
|
|
252
|
+
TWO
|
|
253
|
+
<Button onClick={() => setSelected(2)}>Next</Button>
|
|
254
|
+
<Button onClick={() => setSelected(0)}>Back</Button>
|
|
255
|
+
</ContentFrame>
|
|
256
|
+
<ContentFrame>
|
|
257
|
+
THREE
|
|
258
|
+
<Button onClick={() => setSelected(1)}>Back</Button>
|
|
259
|
+
</ContentFrame>
|
|
260
|
+
</Tabs>
|
|
261
|
+
</Card>
|
|
262
|
+
`}
|
|
263
|
+
providerProps={{
|
|
264
|
+
renderAsComponent: true,
|
|
265
|
+
scope: {
|
|
266
|
+
Tabs,
|
|
267
|
+
ContentFrame,
|
|
268
|
+
Tab,
|
|
269
|
+
InputField,
|
|
270
|
+
Flex,
|
|
271
|
+
Controls,
|
|
272
|
+
NationalityButton,
|
|
273
|
+
Label,
|
|
274
|
+
NationalityButtonPrefix,
|
|
275
|
+
Nothing,
|
|
276
|
+
Button,
|
|
277
|
+
Card: CardFrame,
|
|
278
|
+
Icon,
|
|
279
|
+
},
|
|
280
|
+
}}
|
|
281
|
+
/>
|
|
282
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Meta } from '@storybook/addon-docs';
|
|
2
|
+
import { TabsExample, TabsWithIconsExample, RenderingContentExample, AdditionalControlsExample, StateManagementExample, CrazyTabsExample } from './components';
|
|
3
|
+
|
|
4
|
+
<Meta title="Components/Tabs"/>
|
|
5
|
+
|
|
6
|
+
# Tabs
|
|
7
|
+
|
|
8
|
+
Tabs are used to organize content on a page into sections.
|
|
9
|
+
|
|
10
|
+
For more details, check out the guidelines on [Figma](https://www.figma.com/file/ue1CurHfZ426o2T2l8Dk64/Edvisor-Product-Language?node-id=734%3A7384)
|
|
11
|
+
|
|
12
|
+
## How to Use It
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Tabs, Tab } from '@edvisor/product-language'
|
|
16
|
+
|
|
17
|
+
const [selected, setSelected] = useState(0)
|
|
18
|
+
|
|
19
|
+
<Tabs selected={selected} onChange={setSelected}>
|
|
20
|
+
<Tabs.TabList>
|
|
21
|
+
<Tab>One</Tab>
|
|
22
|
+
<Tab>Two</Tab>
|
|
23
|
+
</Tabs.TabList>
|
|
24
|
+
|
|
25
|
+
<Tabs.Content>
|
|
26
|
+
One
|
|
27
|
+
</Tabs.Content>
|
|
28
|
+
<Tabs.Content>
|
|
29
|
+
Two
|
|
30
|
+
</Tabs.Content>
|
|
31
|
+
</Tabs>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Rendering Content
|
|
35
|
+
|
|
36
|
+
Put whatever you want in the `Content` part of the `Tabs` layout, or style the `Content` slot directly.
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// Add padding to the content slot
|
|
40
|
+
const ContentFrame = styled(Tabs.Content)`
|
|
41
|
+
padding: ${Padding.l};
|
|
42
|
+
`
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
<RenderingContentExample />
|
|
46
|
+
|
|
47
|
+
### Tabs with Icons
|
|
48
|
+
|
|
49
|
+
Add an icon of other simple components to the left of the `Tab` label.
|
|
50
|
+
|
|
51
|
+
<TabsWithIconsExample />
|
|
52
|
+
|
|
53
|
+
### Tabs with additional layout
|
|
54
|
+
|
|
55
|
+
The `Tabs.TabList` slot will render all of its children. Only `Tab` children will be used to control the tabs.
|
|
56
|
+
|
|
57
|
+
<AdditionalControlsExample />
|
|
58
|
+
|
|
59
|
+
Use this to implement additional controls and configuration shared by the tabs.
|
|
60
|
+
|
|
61
|
+
<TabsExample />
|
|
62
|
+
|
|
63
|
+
### Content with additional layout
|
|
64
|
+
|
|
65
|
+
The Tabs component will render any and all children in the order they are given. You can add children between the tabs and content, or you can put the tabs on the bottom.
|
|
66
|
+
|
|
67
|
+
<CrazyTabsExample />
|
|
68
|
+
|
|
69
|
+
### Managing State
|
|
70
|
+
|
|
71
|
+
Pass an integer into the `selected` property of `Tabs` to manage its state.
|
|
72
|
+
|
|
73
|
+
<StateManagementExample />
|
|
74
|
+
|
|
75
|
+
## API
|
|
76
|
+
|
|
77
|
+
### `Tabs`
|
|
78
|
+
|
|
79
|
+
| Prop | Type | Description |
|
|
80
|
+
| ---------- | ---------------------------- | ------------------------------------------------------- |
|
|
81
|
+
| `selected` | `number` | Determines which tab is selected |
|
|
82
|
+
| `onChange` | `(selected: number) => void` | Announces when the currently selected tab should change |
|
|
83
|
+
|
|
84
|
+
### `Tab`
|
|
85
|
+
|
|
86
|
+
Extends `HTMLAttributes<HTMLDivElement>`
|
|
87
|
+
|
|
88
|
+
| Prop | Type | Description |
|
|
89
|
+
| ---------- | ------------ | ------------------------------------------- |
|
|
90
|
+
| `selected` | `boolean?` | Whether this tab is selected |
|
|
91
|
+
| `Prefix` | `Component?` | A component to render in front of the label |
|
|
92
|
+
|
|
93
|
+
## Changelog
|
|
94
|
+
|
|
95
|
+
### [0.0.1]
|
|
96
|
+
- add primary tabs
|
|
97
|
+
- currently we can control the tabs with the "tab" key, but later we should implement the UX described here: https://www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-automatic
|