Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit aca94f8

Browse files
authored
feat(Dropdown): add loading prop (#729)
* feat(Dropdown): add `loading` prop * push cleanups * add changelog entry * add prototype * revert some changes * fix export names * more cleanups * more cleanups * final cleanups * update description * rename to `toggleIndicatorSize` * fix JSON mode * reduce items in example * wip * remove useless components * revert some changes * update comment * fix keys * fix broken UT * remove selector
1 parent d767a49 commit aca94f8

File tree

18 files changed

+284
-138
lines changed

18 files changed

+284
-138
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1919

2020
### BREAKING CHANGES
2121
- Update variable names in themes, add missing sizes @layershifter ([#762](https://github.com/stardust-ui/react/pull/762))
22+
- Rename `toggleButton` prop to `toggleIndicator` and make it visible by default @layershifter ([#729](https://github.com/stardust-ui/react/pull/729))
23+
24+
### Features
25+
- Add `loading` prop for `Dropdown` @layershifter ([#729](https://github.com/stardust-ui/react/pull/729))
2226

2327
<!--------------------------------[ v0.18.0 ]------------------------------- -->
2428
## [v0.18.0](https://github.com/stardust-ui/react/tree/v0.18.0) (2019-01-24)

docs/src/components/CodeSnippet.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
1+
import * as _ from 'lodash'
12
import * as React from 'react'
23

34
import formatCode from '../utils/formatCode'
45
import Editor, { EDITOR_BACKGROUND_COLOR } from './Editor'
56

7+
export type CodeSnippetValue = string | string[] | Object
8+
69
export interface CodeSnippetProps {
710
fitted?: boolean
811
label?: string
9-
mode?: 'jsx' | 'html' | 'sh'
10-
value: string | string[]
12+
mode?: 'json' | 'jsx' | 'html' | 'sh'
13+
value: CodeSnippetValue
1114
style?: React.CSSProperties
1215
}
1316

14-
const joinToString = (stringOrArray: string | string[]) => {
15-
return typeof stringOrArray === 'string' ? stringOrArray : stringOrArray.join('\n')
17+
const normalizeToString = (value: CodeSnippetValue): string => {
18+
if (_.isArray(value)) return value.join('\n')
19+
return _.isObject(value) ? JSON.stringify(value, null, 2) : (value as string)
1620
}
1721

1822
const formatters = {
1923
sh: (val: string = ''): string => val.replace(/^/g, '$ '),
2024
html: (val: string = ''): string => formatCode(val, 'html'),
25+
json: (val: string): string => val,
2126
jsx: (val: string = ''): string => formatCode(val, 'babylon'),
2227
}
2328

24-
const CodeSnippet = ({ fitted, label, value, mode = 'jsx', ...restProps }: CodeSnippetProps) => {
29+
const CodeSnippet = ({ fitted, label, mode, value, ...restProps }: CodeSnippetProps) => {
2530
const format = formatters[mode]
26-
const formattedValue = format(joinToString(value))
31+
const formattedValue = format(normalizeToString(value))
2732
// remove eof line break, they are not helpful for snippets
2833
.replace(/\n$/, '')
2934

@@ -66,4 +71,9 @@ const CodeSnippet = ({ fitted, label, value, mode = 'jsx', ...restProps }: CodeS
6671
</div>
6772
)
6873
}
74+
75+
CodeSnippet.defaultProps = {
76+
mode: 'jsx',
77+
}
78+
6979
export default CodeSnippet

docs/src/components/Editor/Editor.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import AceEditor, { AceEditorProps } from 'react-ace'
55
import * as ace from 'brace'
66
import 'brace/ext/language_tools'
77
import 'brace/mode/html'
8+
import 'brace/mode/json'
89
import 'brace/mode/jsx'
910
import 'brace/mode/sh'
1011
import 'brace/theme/tomorrow_night'
@@ -47,7 +48,7 @@ languageTools.addCompleter(semanticUIReactCompleter)
4748
export interface EditorProps extends AceEditorProps {
4849
active?: boolean
4950
highlightGutterLine?: boolean
50-
mode?: 'html' | 'jsx' | 'sh'
51+
mode?: 'html' | 'jsx' | 'sh' | 'json'
5152
value?: string
5253
showCursor?: boolean
5354
}
@@ -61,7 +62,7 @@ class Editor extends React.Component<EditorProps> {
6162

6263
static propTypes = {
6364
value: PropTypes.string.isRequired,
64-
mode: PropTypes.oneOf(['html', 'jsx', 'sh']),
65+
mode: PropTypes.oneOf(['html', 'json', 'jsx', 'sh']),
6566
active: PropTypes.bool,
6667
showCursor: PropTypes.bool,
6768
}

docs/src/components/Sidebar/Sidebar.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,14 @@ class Sidebar extends React.Component<any, any> {
273273
>
274274
Chat message with popover
275275
</Menu.Item>
276+
<Menu.Item
277+
as={NavLink}
278+
exact
279+
to="/prototype-async-dropdown-search"
280+
activeClassName="active"
281+
>
282+
Async Dropdown Search
283+
</Menu.Item>
276284
<Menu.Item
277285
as={NavLink}
278286
exact
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as React from 'react'
2+
import Knobs from 'docs/src/components/Knobs/Knobs'
3+
4+
type DropdownExampleLoadingKnobsProps = {
5+
loading?: boolean
6+
onKnobChange: () => void
7+
}
8+
9+
const DropdownExampleLoadingKnobs: React.FC<DropdownExampleLoadingKnobsProps> = props => {
10+
const { loading, onKnobChange } = props
11+
12+
return (
13+
<Knobs>
14+
<Knobs.Boolean name="loading" onChange={onKnobChange} value={loading} />
15+
</Knobs>
16+
)
17+
}
18+
19+
DropdownExampleLoadingKnobs.defaultProps = {
20+
loading: true,
21+
}
22+
23+
export default DropdownExampleLoadingKnobs
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Dropdown } from '@stardust-ui/react'
2+
import * as React from 'react'
3+
4+
const inputItems = ['Bruce Wayne', 'Natasha Romanoff', 'Steven Strange', 'Alfred Pennyworth']
5+
6+
const DropdownExampleLoading: React.FC<{ knobs: { loading: boolean } }> = ({ knobs }) => (
7+
<Dropdown
8+
loading={knobs.loading}
9+
loadingMessage="Loading..."
10+
multiple
11+
items={inputItems}
12+
placeholder="Start typing a name"
13+
search
14+
/>
15+
)
16+
17+
export default DropdownExampleLoading
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as React from 'react'
2+
3+
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
4+
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
5+
6+
const State = () => (
7+
<ExampleSection title="State">
8+
<ComponentExample
9+
title="Loading"
10+
description="A dropdown can show that it is currently loading data."
11+
examplePath="components/Dropdown/State/DropdownExampleLoading"
12+
/>
13+
</ExampleSection>
14+
)
15+
16+
export default State

docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx

Lines changed: 0 additions & 55 deletions
This file was deleted.

docs/src/examples/components/Dropdown/Variations/index.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@ const Variations = () => (
1414
description="A multiple search dropdown that fits the width of the container."
1515
examplePath="components/Dropdown/Variations/DropdownExampleMultipleSearchFluid"
1616
/>
17-
<ComponentExample
18-
title="Multiple Search with Toggle Button"
19-
description="A multiple search dropdown with toggle button that shows/hides the items list."
20-
examplePath="components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton"
21-
/>
2217
</ExampleSection>
2318
)
2419

docs/src/examples/components/Dropdown/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import * as React from 'react'
22

33
import Types from './Types'
44
import Variations from './Variations'
5+
import State from './State'
56
import Usage from './Usage'
67

78
const DropdownExamples = () => (
89
<div>
910
<Types />
1011
<Variations />
12+
<State />
1113
<Usage />
1214
</div>
1315
)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { Divider, Dropdown, DropdownProps, Header, Loader, Segment } from '@stardust-ui/react'
2+
import * as faker from 'faker'
3+
import * as _ from 'lodash'
4+
import * as React from 'react'
5+
6+
import CodeSnippet from '../../components/CodeSnippet'
7+
8+
// ----------------------------------------
9+
// Types
10+
// ----------------------------------------
11+
type Entry = {
12+
header: string
13+
image: string
14+
content: string
15+
}
16+
17+
interface SearchPageState {
18+
loading: boolean
19+
items: Entry[]
20+
searchQuery: string
21+
value: Entry[]
22+
}
23+
24+
// ----------------------------------------
25+
// Mock Data
26+
// ----------------------------------------
27+
const createEntry = (): Entry => ({
28+
image: faker.internet.avatar(),
29+
header: `${faker.name.firstName()} ${faker.name.lastName()}`,
30+
content: faker.commerce.department(),
31+
})
32+
33+
// ----------------------------------------
34+
// Prototype Search Page View
35+
// ----------------------------------------
36+
class AsyncDropdownSearch extends React.Component<{}, SearchPageState> {
37+
state = {
38+
loading: false,
39+
searchQuery: '',
40+
items: [],
41+
value: [],
42+
}
43+
44+
searchTimer: number
45+
46+
handleSelectedChange = (e: React.SyntheticEvent, { searchQuery, value }: DropdownProps) => {
47+
this.setState({ value: value as Entry[], searchQuery })
48+
}
49+
50+
handleSearchQueryChange = (e: React.SyntheticEvent, { searchQuery }: DropdownProps) => {
51+
this.setState({ searchQuery })
52+
this.fetchItems()
53+
}
54+
55+
fetchItems = () => {
56+
clearTimeout(this.searchTimer)
57+
this.setState({ loading: true })
58+
59+
this.searchTimer = setTimeout(() => {
60+
this.setState(prevState => ({
61+
loading: false,
62+
items: [...prevState.items, ..._.times<Entry>(10, createEntry)],
63+
}))
64+
}, 2000)
65+
}
66+
67+
render() {
68+
const { items, loading, searchQuery, value } = this.state
69+
70+
return (
71+
<div style={{ margin: 20 }}>
72+
<Segment>
73+
<Header content="Async Dropdown Search" />
74+
<p>Use the field to perform a simulated search.</p>
75+
</Segment>
76+
77+
<Segment>
78+
<Dropdown
79+
fluid
80+
items={items}
81+
loading={loading}
82+
loadingMessage={{
83+
content: <Loader label="Loading..." labelPosition="end" size="larger" />,
84+
}}
85+
multiple
86+
onSearchQueryChange={this.handleSearchQueryChange}
87+
onSelectedChange={this.handleSelectedChange}
88+
placeholder="Try to enter something..."
89+
search
90+
searchQuery={searchQuery}
91+
toggleIndicator={false}
92+
value={value}
93+
/>
94+
<Divider />
95+
<CodeSnippet mode="json" value={this.state} />
96+
</Segment>
97+
</div>
98+
)
99+
}
100+
}
101+
102+
export default AsyncDropdownSearch
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './AsyncDropdownSearch'

docs/src/routes.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ const Router = () => (
6060
path="/prototype-search-page"
6161
component={require('./prototypes/SearchPage/index').default}
6262
/>,
63+
<DocsLayout
64+
exact
65+
key="/prototype-async-dropdown-search"
66+
path="/prototype-async-dropdown-search"
67+
component={require('./prototypes/AsyncDropdownSearch/index').default}
68+
/>,
6369
<DocsLayout
6470
exact
6571
key="/prototype-popups"

0 commit comments

Comments
 (0)