@junyiacademy/ui-test 0.0.6 → 0.0.10
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/declarations/libs/ui/src/index.d.ts +4 -1
- package/declarations/libs/ui/src/lib/menu-item/SelectMenuItem.d.ts +4 -4
- package/declarations/libs/ui/src/lib/select/OutlinedSelect.d.ts +11 -6
- package/declarations/libs/ui/src/lib/select/StandardSelect.d.ts +2 -6
- package/dist/libs/ui/src/index.js +9 -3
- package/dist/libs/ui/src/lib/TopicFilter.js +22 -26
- package/dist/libs/ui/src/lib/menu-item/SelectMenuItem.js +15 -12
- package/dist/libs/ui/src/lib/select/OutlinedSelect.js +92 -50
- package/dist/libs/ui/src/lib/select/StandardSelect.js +11 -19
- package/dist/libs/ui/src/lib/selection-item/SelectionItem.js +3 -2
- package/package.json +1 -1
- package/src/index.ts +4 -1
- package/src/lib/TopicFilter.spec.tsx +10 -0
- package/src/lib/TopicFilter.stories.tsx +82 -0
- package/src/lib/TopicFilter.tsx +204 -0
- package/src/lib/menu-item/SelectMenuItem.spec.tsx +10 -0
- package/src/lib/menu-item/SelectMenuItem.stories.tsx +44 -0
- package/src/lib/menu-item/SelectMenuItem.tsx +45 -0
- package/src/lib/radio/Radio.stories.tsx +3 -2
- package/src/lib/select/OutlinedSelect.spec.tsx +10 -0
- package/src/lib/select/OutlinedSelect.stories.tsx +238 -0
- package/src/lib/select/OutlinedSelect.tsx +219 -0
- package/src/lib/select/StandardSelect.stories.tsx +221 -0
- package/src/lib/select/StandardSelect.tsx +181 -0
- package/src/lib/text-field/TextField.stories.tsx +35 -1
- package/src/utils/topicTree.ts +91 -1
- package/src/lib/selection-item/SelectionItem.tsx +0 -37
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React, { ChangeEvent } from 'react'
|
|
2
|
+
import { Theme, styled } from '@material-ui/core/styles'
|
|
3
|
+
import {
|
|
4
|
+
InputLabel,
|
|
5
|
+
FormControl,
|
|
6
|
+
Select,
|
|
7
|
+
OutlinedInput,
|
|
8
|
+
SelectProps,
|
|
9
|
+
OutlinedInputProps,
|
|
10
|
+
FormHelperText,
|
|
11
|
+
} from '@material-ui/core'
|
|
12
|
+
|
|
13
|
+
// self-defined-components
|
|
14
|
+
const PREFIX = 'JuiOutlinedSelect'
|
|
15
|
+
|
|
16
|
+
const classes = {
|
|
17
|
+
inputLabelFocused: `${PREFIX}-inputLabelFocused`,
|
|
18
|
+
inputLabelOutlined: `${PREFIX}-inputLabelOutlined`,
|
|
19
|
+
inputLabelMarginDense: `${PREFIX}-inputLabelMarginDense`,
|
|
20
|
+
inputLabelShrink: `${PREFIX}-inputLabelShrink`,
|
|
21
|
+
inputLabelError: `${PREFIX}-inputLabelError`,
|
|
22
|
+
inputLabelDisabled: `${PREFIX}-inputLabelDisabled`,
|
|
23
|
+
outlineInputInput: `${PREFIX}-input`,
|
|
24
|
+
outlineInputInputMarginDense: `${PREFIX}-inputMarginDense`,
|
|
25
|
+
outlineInputNotchedOutline: `${PREFIX}-notchedOutline`,
|
|
26
|
+
outlineInputDisabled: `${PREFIX}-inputDisabled`,
|
|
27
|
+
outlineInputError: `${PREFIX}-outlinedInputError`,
|
|
28
|
+
selectPaper: `${PREFIX}-menuPaper`,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface StyledFormControlProps {
|
|
32
|
+
color: 'primary' | 'secondary'
|
|
33
|
+
width: number | 'auto'
|
|
34
|
+
theme?: Theme
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const StyledFormControl = styled(
|
|
38
|
+
({ color: _color, width: _width, ...props }) => <FormControl {...props} />
|
|
39
|
+
)(({ color, width, theme }: StyledFormControlProps) => ({
|
|
40
|
+
margin: theme.spacing(1),
|
|
41
|
+
width: width,
|
|
42
|
+
backgroundColor: 'white',
|
|
43
|
+
'&:hover': {
|
|
44
|
+
[`& :not(.${classes.outlineInputDisabled}):not(.${classes.outlineInputError}) .${classes.outlineInputNotchedOutline}`]: {
|
|
45
|
+
borderColor: theme.palette[color].main,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
interface StyledInputLabelProps {
|
|
51
|
+
color: 'primary' | 'secondary'
|
|
52
|
+
theme?: Theme
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const StyledInputLabel = styled(({ color: _color, ...props }) => (
|
|
56
|
+
<InputLabel
|
|
57
|
+
classes={{
|
|
58
|
+
outlined: classes.inputLabelOutlined,
|
|
59
|
+
marginDense: classes.inputLabelMarginDense,
|
|
60
|
+
shrink: classes.inputLabelShrink,
|
|
61
|
+
focused: classes.inputLabelFocused,
|
|
62
|
+
disabled: classes.inputLabelDisabled,
|
|
63
|
+
error: classes.inputLabelError,
|
|
64
|
+
}}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
))(({ color, theme }: StyledInputLabelProps) => ({
|
|
68
|
+
color: theme.palette.text.secondary,
|
|
69
|
+
[`&.${classes.inputLabelOutlined}`]: {
|
|
70
|
+
[`&:not(.${classes.inputLabelDisabled}) .${classes.inputLabelFocused}`]: {
|
|
71
|
+
color: theme.palette.action.active,
|
|
72
|
+
},
|
|
73
|
+
[`&.${classes.inputLabelMarginDense}:not(.${classes.inputLabelShrink})`]: {
|
|
74
|
+
transform: 'translate(12px, 16px) scale(1)',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
[`&.${classes.inputLabelShrink}:not(.${classes.inputLabelError}):not(.${classes.inputLabelDisabled}).${classes.inputLabelFocused}`]: {
|
|
78
|
+
color: theme.palette[color].main,
|
|
79
|
+
},
|
|
80
|
+
[`&.${classes.inputLabelShrink}`]: {
|
|
81
|
+
backgroundColor: '#ffffff',
|
|
82
|
+
padding: '0 2px',
|
|
83
|
+
},
|
|
84
|
+
}))
|
|
85
|
+
|
|
86
|
+
interface StyledOutlinedInputProps {
|
|
87
|
+
theme?: Theme
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const StyledOutlinedInput = styled((props) => (
|
|
91
|
+
<OutlinedInput
|
|
92
|
+
classes={{
|
|
93
|
+
input: classes.outlineInputInput,
|
|
94
|
+
inputMarginDense: classes.outlineInputInputMarginDense,
|
|
95
|
+
notchedOutline: classes.outlineInputNotchedOutline,
|
|
96
|
+
disabled: classes.outlineInputDisabled,
|
|
97
|
+
error: classes.outlineInputError,
|
|
98
|
+
}}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
))(({ theme }: StyledOutlinedInputProps) => ({
|
|
102
|
+
[`& .${classes.outlineInputInput}`]: {
|
|
103
|
+
color: theme.palette.text.primary,
|
|
104
|
+
},
|
|
105
|
+
[`& .${classes.outlineInputInputMarginDense}`]: {
|
|
106
|
+
padding: '14.5px 15px 14.5px 12px',
|
|
107
|
+
},
|
|
108
|
+
}))
|
|
109
|
+
|
|
110
|
+
interface StyledSelectProps {
|
|
111
|
+
paperMaxHeight: number | 'auto'
|
|
112
|
+
hasAdornment: boolean
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const StyledSelect = styled(
|
|
116
|
+
({
|
|
117
|
+
paperMaxHeight: _paperMaxHeight,
|
|
118
|
+
hasAdornment: _hasAdornment,
|
|
119
|
+
className,
|
|
120
|
+
...props
|
|
121
|
+
}) => (
|
|
122
|
+
<Select
|
|
123
|
+
MenuProps={{
|
|
124
|
+
disableAutoFocusItem: true,
|
|
125
|
+
anchorOrigin: {
|
|
126
|
+
vertical: 2,
|
|
127
|
+
horizontal: 'left',
|
|
128
|
+
},
|
|
129
|
+
transformOrigin: {
|
|
130
|
+
vertical: 'top',
|
|
131
|
+
horizontal: 'left',
|
|
132
|
+
},
|
|
133
|
+
getContentAnchorEl: null,
|
|
134
|
+
classes: { paper: className },
|
|
135
|
+
}}
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
)
|
|
139
|
+
)(({ hasAdornment, paperMaxHeight }: StyledSelectProps) => ({
|
|
140
|
+
'&': {
|
|
141
|
+
maxHeight: paperMaxHeight,
|
|
142
|
+
left: hasAdornment ? '24px !important' : '70px',
|
|
143
|
+
},
|
|
144
|
+
}))
|
|
145
|
+
|
|
146
|
+
export interface OutlinedSelectProps extends SelectProps {
|
|
147
|
+
color?: 'primary' | 'secondary'
|
|
148
|
+
size?: 'medium' | 'small'
|
|
149
|
+
width?: number | 'auto'
|
|
150
|
+
paperMaxHeight?: number | 'auto'
|
|
151
|
+
error?: boolean
|
|
152
|
+
hasLabel?: boolean
|
|
153
|
+
hasShrink?: boolean
|
|
154
|
+
placeholder: string
|
|
155
|
+
helperText?: string
|
|
156
|
+
disabled?: boolean
|
|
157
|
+
SelectProps?: object | Partial<SelectProps>
|
|
158
|
+
OutlinedInputProps?: Partial<OutlinedInputProps> & {
|
|
159
|
+
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const OutlinedSelect = ({
|
|
164
|
+
placeholder,
|
|
165
|
+
SelectProps,
|
|
166
|
+
OutlinedInputProps,
|
|
167
|
+
children,
|
|
168
|
+
color = 'primary',
|
|
169
|
+
size = 'medium',
|
|
170
|
+
width = 'auto',
|
|
171
|
+
paperMaxHeight = 'auto',
|
|
172
|
+
error = false,
|
|
173
|
+
hasLabel = true,
|
|
174
|
+
hasShrink = false,
|
|
175
|
+
helperText = '',
|
|
176
|
+
value = '',
|
|
177
|
+
disabled = false,
|
|
178
|
+
}: OutlinedSelectProps) => {
|
|
179
|
+
const hasAdornment = !!OutlinedInputProps?.startAdornment
|
|
180
|
+
const hasHelperText = !!helperText
|
|
181
|
+
return (
|
|
182
|
+
<StyledFormControl
|
|
183
|
+
size={size}
|
|
184
|
+
width={width}
|
|
185
|
+
disabled={disabled}
|
|
186
|
+
error={error}
|
|
187
|
+
color={color}
|
|
188
|
+
>
|
|
189
|
+
{hasLabel && (
|
|
190
|
+
<StyledInputLabel
|
|
191
|
+
color={color}
|
|
192
|
+
variant='outlined'
|
|
193
|
+
shrink={hasShrink ? true : undefined}
|
|
194
|
+
>
|
|
195
|
+
{placeholder}
|
|
196
|
+
</StyledInputLabel>
|
|
197
|
+
)}
|
|
198
|
+
<StyledSelect
|
|
199
|
+
value={value}
|
|
200
|
+
paperMaxHeight={paperMaxHeight}
|
|
201
|
+
hasAdornment={hasAdornment}
|
|
202
|
+
input={
|
|
203
|
+
<StyledOutlinedInput
|
|
204
|
+
color={color}
|
|
205
|
+
label={hasLabel ? placeholder : undefined}
|
|
206
|
+
disabled={disabled}
|
|
207
|
+
{...OutlinedInputProps}
|
|
208
|
+
/>
|
|
209
|
+
}
|
|
210
|
+
{...SelectProps}
|
|
211
|
+
>
|
|
212
|
+
{children}
|
|
213
|
+
</StyledSelect>
|
|
214
|
+
{hasHelperText && <FormHelperText>{helperText}</FormHelperText>}
|
|
215
|
+
</StyledFormControl>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export default OutlinedSelect
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Story, Meta } from '@storybook/react'
|
|
3
|
+
import { Theme, styled } from '@material-ui/core/styles'
|
|
4
|
+
import { InputAdornment } from '@material-ui/core'
|
|
5
|
+
import { Visibility } from '@material-ui/icons'
|
|
6
|
+
import { StandardSelect, StandardSelectProps } from './StandardSelect'
|
|
7
|
+
import SelectMenuItem from '../menu-item/SelectMenuItem'
|
|
8
|
+
|
|
9
|
+
const PLACEHOLDER = '請選擇'
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
component: StandardSelect,
|
|
13
|
+
title: 'StandardSelect',
|
|
14
|
+
argTypes: {
|
|
15
|
+
color: {
|
|
16
|
+
type: { name: 'string', required: false },
|
|
17
|
+
description:
|
|
18
|
+
'The color of the component. It supports those theme colors that make sense for this component.',
|
|
19
|
+
table: {
|
|
20
|
+
type: { summary: 'primary | secondary' },
|
|
21
|
+
defaultValue: { summary: 'primary' },
|
|
22
|
+
},
|
|
23
|
+
options: ['primary', 'secondary'],
|
|
24
|
+
control: { type: 'radio' },
|
|
25
|
+
},
|
|
26
|
+
size: {
|
|
27
|
+
type: { name: 'string', required: false },
|
|
28
|
+
description: `Adjust size`,
|
|
29
|
+
table: {
|
|
30
|
+
type: { summary: 'medium | small' },
|
|
31
|
+
defaultValue: { summary: 'medium' },
|
|
32
|
+
},
|
|
33
|
+
options: ['small', 'medium'],
|
|
34
|
+
control: { type: 'radio' },
|
|
35
|
+
},
|
|
36
|
+
width: {
|
|
37
|
+
type: { name: 'number', required: false },
|
|
38
|
+
description: `Adjust width`,
|
|
39
|
+
table: {
|
|
40
|
+
type: { summary: 'number' },
|
|
41
|
+
defaultValue: { summary: 'auto' },
|
|
42
|
+
},
|
|
43
|
+
control: { type: 'number' },
|
|
44
|
+
},
|
|
45
|
+
paperMaxHeight: {
|
|
46
|
+
type: { name: 'number', required: false },
|
|
47
|
+
description: `Adjust select menu paper max height.`,
|
|
48
|
+
table: {
|
|
49
|
+
type: { summary: 'number' },
|
|
50
|
+
defaultValue: { summary: 'auto' },
|
|
51
|
+
},
|
|
52
|
+
control: { type: 'number' },
|
|
53
|
+
},
|
|
54
|
+
hasShrink: {
|
|
55
|
+
type: { name: 'boolean', required: false },
|
|
56
|
+
description: 'If true, the label is displayed and shrunk.',
|
|
57
|
+
table: {
|
|
58
|
+
type: { summary: 'boolean' },
|
|
59
|
+
defaultValue: { summary: true },
|
|
60
|
+
},
|
|
61
|
+
control: { type: 'boolean' },
|
|
62
|
+
},
|
|
63
|
+
placeholder: {
|
|
64
|
+
type: { name: 'string', required: true },
|
|
65
|
+
description: `The label title`,
|
|
66
|
+
table: {
|
|
67
|
+
type: { summary: 'string' },
|
|
68
|
+
defaultValue: { summary: '請選擇' },
|
|
69
|
+
},
|
|
70
|
+
control: { type: 'text' },
|
|
71
|
+
},
|
|
72
|
+
value: {
|
|
73
|
+
type: { name: 'any', required: false },
|
|
74
|
+
description: `The input value. Providing an empty string will select no options. This prop is required when the native prop is false (default). Set to an empty string '' if you don't want any of the available options to be selected.
|
|
75
|
+
If the value is an object it must have reference equality with the option in order to be selected. If the value is not an object, the string representation must match with the string representation of the option in order to be selected.`,
|
|
76
|
+
table: {
|
|
77
|
+
type: { summary: 'any' },
|
|
78
|
+
defaultValue: { summary: '' },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
disabled: {
|
|
82
|
+
type: { name: 'boolean', required: false },
|
|
83
|
+
description: 'If true, the input element will be disabled.',
|
|
84
|
+
table: {
|
|
85
|
+
type: { summary: 'boolean' },
|
|
86
|
+
defaultValue: { summary: false },
|
|
87
|
+
},
|
|
88
|
+
control: { type: 'boolean' },
|
|
89
|
+
},
|
|
90
|
+
error: {
|
|
91
|
+
type: { name: 'boolean', required: false },
|
|
92
|
+
description: 'If true, the label will be displayed in an error state.',
|
|
93
|
+
table: {
|
|
94
|
+
type: { summary: 'boolean' },
|
|
95
|
+
defaultValue: { summary: false },
|
|
96
|
+
},
|
|
97
|
+
control: { type: 'boolean' },
|
|
98
|
+
},
|
|
99
|
+
helperText: {
|
|
100
|
+
type: { name: 'string', required: true },
|
|
101
|
+
description: `Display the helper text.`,
|
|
102
|
+
table: {
|
|
103
|
+
type: { summary: 'string' },
|
|
104
|
+
defaultValue: { summary: '' },
|
|
105
|
+
},
|
|
106
|
+
control: { type: 'text' },
|
|
107
|
+
},
|
|
108
|
+
InputProps: {
|
|
109
|
+
type: { name: 'any', required: false },
|
|
110
|
+
description: 'Attributes applied to inner Input element.',
|
|
111
|
+
table: {
|
|
112
|
+
type: { summary: 'any' },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
SelectProps: {
|
|
116
|
+
type: { name: 'any', required: false },
|
|
117
|
+
description: 'Attributes applied to inner Select element.',
|
|
118
|
+
table: {
|
|
119
|
+
type: { summary: 'any' },
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
} as Meta
|
|
124
|
+
|
|
125
|
+
const PREFIX = 'JuiStandardSelect'
|
|
126
|
+
|
|
127
|
+
const classes = {
|
|
128
|
+
inputAdornmentRoot: `${PREFIX}-inputAdornmentRoot`,
|
|
129
|
+
}
|
|
130
|
+
interface StyledInputAdornmentProps {
|
|
131
|
+
theme: Theme
|
|
132
|
+
disabled: boolean
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const StyledInputAdornment = styled(({ disabled: _disabled, ...props }) => (
|
|
136
|
+
<InputAdornment
|
|
137
|
+
classes={{
|
|
138
|
+
root: classes.inputAdornmentRoot,
|
|
139
|
+
}}
|
|
140
|
+
{...props}
|
|
141
|
+
/>
|
|
142
|
+
))(({ disabled, theme }: StyledInputAdornmentProps) => ({
|
|
143
|
+
[`&.${classes.inputAdornmentRoot}`]: {
|
|
144
|
+
color: disabled
|
|
145
|
+
? theme.palette.action.disabled
|
|
146
|
+
: theme.palette.action.active,
|
|
147
|
+
},
|
|
148
|
+
}))
|
|
149
|
+
|
|
150
|
+
const StandardSelectWithMenu = (props: StandardSelectProps) => {
|
|
151
|
+
const [item, setItem] = useState<string>('')
|
|
152
|
+
|
|
153
|
+
const handleChange = (event) => {
|
|
154
|
+
setItem(event.target.value)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<StandardSelect
|
|
159
|
+
value={item}
|
|
160
|
+
SelectProps={{
|
|
161
|
+
onChange: (e) => {
|
|
162
|
+
handleChange(e)
|
|
163
|
+
},
|
|
164
|
+
}}
|
|
165
|
+
{...props}
|
|
166
|
+
>
|
|
167
|
+
<SelectMenuItem width={300} value='' disabled>
|
|
168
|
+
{PLACEHOLDER}
|
|
169
|
+
</SelectMenuItem>
|
|
170
|
+
<SelectMenuItem width={300} value={'Test'}>
|
|
171
|
+
This is a select menu item, This is a select menu item
|
|
172
|
+
</SelectMenuItem>
|
|
173
|
+
<SelectMenuItem width={300} value={'Example'}>
|
|
174
|
+
Example
|
|
175
|
+
</SelectMenuItem>
|
|
176
|
+
</StandardSelect>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const ValueOnlyStory: Story<StandardSelectProps> = (args) => (
|
|
181
|
+
<StandardSelectWithMenu {...args} />
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
export const ValueOnly = ValueOnlyStory.bind({})
|
|
185
|
+
|
|
186
|
+
ValueOnly.args = {
|
|
187
|
+
color: 'primary',
|
|
188
|
+
size: 'medium',
|
|
189
|
+
width: 220,
|
|
190
|
+
paperMaxHeight: 300,
|
|
191
|
+
hasShrink: false,
|
|
192
|
+
placeholder: PLACEHOLDER,
|
|
193
|
+
helperText: 'test',
|
|
194
|
+
disabled: false,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const WithPrefixStory: Story<StandardSelectProps> = (args) => (
|
|
198
|
+
<StandardSelectWithMenu
|
|
199
|
+
InputProps={{
|
|
200
|
+
startAdornment: (
|
|
201
|
+
<StyledInputAdornment position='start' disabled={args.disabled}>
|
|
202
|
+
<Visibility />
|
|
203
|
+
</StyledInputAdornment>
|
|
204
|
+
),
|
|
205
|
+
}}
|
|
206
|
+
{...args}
|
|
207
|
+
/>
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
export const WithPrefix = WithPrefixStory.bind({})
|
|
211
|
+
|
|
212
|
+
WithPrefix.args = {
|
|
213
|
+
color: 'primary',
|
|
214
|
+
size: 'medium',
|
|
215
|
+
width: 300,
|
|
216
|
+
paperMaxHeight: 300,
|
|
217
|
+
hasShrink: false,
|
|
218
|
+
placeholder: PLACEHOLDER,
|
|
219
|
+
helperText: 'test',
|
|
220
|
+
disabled: false,
|
|
221
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Theme, styled } from '@material-ui/core/styles'
|
|
3
|
+
import {
|
|
4
|
+
InputLabel,
|
|
5
|
+
FormControl,
|
|
6
|
+
Select,
|
|
7
|
+
SelectProps,
|
|
8
|
+
Input,
|
|
9
|
+
InputProps,
|
|
10
|
+
FormHelperText,
|
|
11
|
+
InputAdornment,
|
|
12
|
+
} from '@material-ui/core'
|
|
13
|
+
|
|
14
|
+
// self-defined-components
|
|
15
|
+
const PREFIX = 'JuiStandardSelect'
|
|
16
|
+
|
|
17
|
+
const classes = {
|
|
18
|
+
inputLabelFocused: `${PREFIX}-inputLabelFocused`,
|
|
19
|
+
inputLabelMarginDense: `${PREFIX}-inputLabelMarginDense`,
|
|
20
|
+
inputLabelError: `${PREFIX}-inputLabelError`,
|
|
21
|
+
inputUnderline: `${PREFIX}-inputUnderline`,
|
|
22
|
+
inputError: `${PREFIX}-inputError`,
|
|
23
|
+
inputDisabled: `${PREFIX}-inputDisabled`,
|
|
24
|
+
inputAdornmentRoot: `${PREFIX}-inputAdornmentRoot`,
|
|
25
|
+
selectPaper: `${PREFIX}-menuPaper`,
|
|
26
|
+
selectDisabled: `${PREFIX}-selectDisabled`,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface StyledFormControlProps {
|
|
30
|
+
color: 'primary' | 'secondary'
|
|
31
|
+
width: number | 'auto'
|
|
32
|
+
theme?: Theme
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const StyledFormControl = styled(({ width: _width, ...props }) => (
|
|
36
|
+
<FormControl {...props} />
|
|
37
|
+
))(({ width, theme }: StyledFormControlProps) => ({
|
|
38
|
+
width,
|
|
39
|
+
margin: theme.spacing(0, 4, 4, 4),
|
|
40
|
+
}))
|
|
41
|
+
|
|
42
|
+
interface StyledInputLabelProps {
|
|
43
|
+
color: 'primary' | 'secondary'
|
|
44
|
+
theme?: Theme
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const StyledInputLabel = styled(({ color: _color, ...props }) => (
|
|
48
|
+
<InputLabel
|
|
49
|
+
classes={{
|
|
50
|
+
focused: classes.inputLabelFocused,
|
|
51
|
+
error: classes.inputLabelError,
|
|
52
|
+
}}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
))(({ color, theme }: StyledInputLabelProps) => ({
|
|
56
|
+
color: theme.palette.text.disabled,
|
|
57
|
+
margin: theme.spacing(0, 10, 1.5, 0),
|
|
58
|
+
[`&.${classes.inputLabelFocused}`]: {
|
|
59
|
+
color: theme.palette[color].main,
|
|
60
|
+
},
|
|
61
|
+
[`&.${classes.inputLabelError}`]: {
|
|
62
|
+
color: theme.palette.error.main,
|
|
63
|
+
},
|
|
64
|
+
}))
|
|
65
|
+
|
|
66
|
+
interface StyledSelectProps {
|
|
67
|
+
paperMaxHeight: number | 'auto'
|
|
68
|
+
hasAdornment: boolean
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const StyledSelect = styled(
|
|
72
|
+
({
|
|
73
|
+
paperMaxHeight: _selectPaperHeight,
|
|
74
|
+
hasAdornment: _hasAdornment,
|
|
75
|
+
className,
|
|
76
|
+
...props
|
|
77
|
+
}) => (
|
|
78
|
+
<Select
|
|
79
|
+
MenuProps={{
|
|
80
|
+
classes: { paper: className },
|
|
81
|
+
transformOrigin: {
|
|
82
|
+
vertical: 'top',
|
|
83
|
+
horizontal: 'left',
|
|
84
|
+
},
|
|
85
|
+
getContentAnchorEl: null,
|
|
86
|
+
}}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
)(({ hasAdornment, paperMaxHeight }: StyledSelectProps) => ({
|
|
91
|
+
'&&': {
|
|
92
|
+
maxHeight: paperMaxHeight,
|
|
93
|
+
left: hasAdornment ? '48px !important' : '70px',
|
|
94
|
+
},
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
interface StyledInputProps {
|
|
98
|
+
color: 'primary' | 'secondary'
|
|
99
|
+
theme: Theme
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const StyledInput = styled(({ color: _color, ...props }) => (
|
|
103
|
+
<Input
|
|
104
|
+
classes={{
|
|
105
|
+
disabled: classes.inputDisabled,
|
|
106
|
+
underline: classes.inputUnderline,
|
|
107
|
+
error: classes.inputError,
|
|
108
|
+
}}
|
|
109
|
+
{...props}
|
|
110
|
+
/>
|
|
111
|
+
))(({ color, theme }: StyledInputProps) => ({
|
|
112
|
+
color: theme.palette.text.primary,
|
|
113
|
+
[`&.${classes.inputUnderline}:not(.${classes.inputDisabled}):not(.${classes.inputError})`]: {
|
|
114
|
+
[`&:after,&:hover:before`]: {
|
|
115
|
+
borderBottomColor: theme.palette[color].main,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
[`&.${classes.inputUnderline}.${classes.inputError}:not(.${classes.inputDisabled})`]: {
|
|
119
|
+
[`&:after,&:hover:before`]: {
|
|
120
|
+
borderBottomColor: theme.palette.error.main,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}))
|
|
124
|
+
|
|
125
|
+
export interface StandardSelectProps extends SelectProps {
|
|
126
|
+
color?: 'primary' | 'secondary'
|
|
127
|
+
size?: 'medium' | 'small'
|
|
128
|
+
width?: number | 'auto'
|
|
129
|
+
paperMaxHeight?: number | 'auto'
|
|
130
|
+
error?: boolean
|
|
131
|
+
hasShrink?: boolean
|
|
132
|
+
placeholder: string
|
|
133
|
+
helperText?: string
|
|
134
|
+
InputProps?: object & Partial<InputProps>
|
|
135
|
+
disabled?: boolean
|
|
136
|
+
SelectProps?: object | Partial<SelectProps>
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function StandardSelect({
|
|
140
|
+
placeholder,
|
|
141
|
+
helperText,
|
|
142
|
+
InputProps,
|
|
143
|
+
SelectProps,
|
|
144
|
+
children,
|
|
145
|
+
color = 'primary',
|
|
146
|
+
size = 'medium',
|
|
147
|
+
width = 'auto',
|
|
148
|
+
paperMaxHeight = 'auto',
|
|
149
|
+
error = false,
|
|
150
|
+
hasShrink = false,
|
|
151
|
+
value = '',
|
|
152
|
+
disabled = false,
|
|
153
|
+
}: StandardSelectProps) {
|
|
154
|
+
const hasAdornment = !!InputProps?.startAdornment
|
|
155
|
+
const hasHelperText = !!helperText
|
|
156
|
+
return (
|
|
157
|
+
<StyledFormControl
|
|
158
|
+
color={color}
|
|
159
|
+
size={size}
|
|
160
|
+
width={width}
|
|
161
|
+
disabled={disabled}
|
|
162
|
+
error={error}
|
|
163
|
+
>
|
|
164
|
+
<StyledInputLabel color={color} shrink={hasShrink ? true : undefined}>
|
|
165
|
+
{placeholder}
|
|
166
|
+
</StyledInputLabel>
|
|
167
|
+
<StyledSelect
|
|
168
|
+
value={value}
|
|
169
|
+
paperMaxHeight={paperMaxHeight}
|
|
170
|
+
hasAdornment={hasAdornment}
|
|
171
|
+
input={<StyledInput color={color} {...InputProps} />}
|
|
172
|
+
{...SelectProps}
|
|
173
|
+
>
|
|
174
|
+
{children}
|
|
175
|
+
</StyledSelect>
|
|
176
|
+
{hasHelperText && <FormHelperText>{helperText}</FormHelperText>}
|
|
177
|
+
</StyledFormControl>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export default StandardSelect
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useEffect } from 'react'
|
|
2
2
|
import { Story, Meta } from '@storybook/react'
|
|
3
3
|
import { InputAdornment, TextFieldProps } from '@material-ui/core'
|
|
4
4
|
import { Visibility } from '@material-ui/icons'
|
|
@@ -77,6 +77,40 @@ ValueOnly.args = {
|
|
|
77
77
|
label: 'Which UI?',
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
const TextFieldWithError = (props: TextFieldProps) => {
|
|
81
|
+
const [value, setValue] = React.useState('')
|
|
82
|
+
const [isError, setIsError] = React.useState(false)
|
|
83
|
+
|
|
84
|
+
const handleChange = (event) => {
|
|
85
|
+
setValue(event.target.value)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (value.length > 3) {
|
|
90
|
+
setIsError(true)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
setIsError(false)
|
|
94
|
+
return
|
|
95
|
+
}, [value])
|
|
96
|
+
|
|
97
|
+
return <TextField error={isError} onChange={handleChange} {...props} />
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const WithErrorStory: Story<TextFieldProps> = (args) => (
|
|
101
|
+
<TextFieldWithError {...args} />
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
export const WithError = WithErrorStory.bind({})
|
|
105
|
+
|
|
106
|
+
WithError.args = {
|
|
107
|
+
variant: 'standard',
|
|
108
|
+
color: 'primary',
|
|
109
|
+
disabled: false,
|
|
110
|
+
size: 'small',
|
|
111
|
+
label: 'No more than 3 words',
|
|
112
|
+
}
|
|
113
|
+
|
|
80
114
|
const WithSuffixStory: Story<TextFieldProps> = (args) => (
|
|
81
115
|
<TextField
|
|
82
116
|
{...args}
|