/
Spotlight.story.tsx
234 lines (224 loc) · 7.25 KB
/
Spotlight.story.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/* eslint-disable no-console */
import React, { useEffect } from 'react';
import { storiesOf } from '@storybook/react';
import { Button, Box } from '@mantine/core';
import { IconSearch } from '@tabler/icons';
import {
SpotlightProvider,
useSpotlight,
SpotlightProviderProps,
SpotlightAction,
openSpotlight,
registerSpotlightActions,
} from '.';
const DEFAULT_ACTIONS: SpotlightAction[] = [
{
title: 'Action 1',
description: 'This action will trigger something important',
onTrigger: () => console.log('Action 1'),
keywords: 'home, work',
},
{ title: 'Action 2', onTrigger: () => console.log('Action 2'), keywords: ['hello', 'there'] },
];
const LARGE_ACTIONS_SET: SpotlightAction[] = Array(100)
.fill(0)
.map((_, index) => ({
title: `Action ${index + 1}`,
onTrigger: () => console.log('Action'),
}));
function RegisterInEffect() {
useEffect(() => {
registerSpotlightActions([
{ title: 'Effect action 1', onTrigger: () => console.log('Effect action 1') },
{ title: 'Effect action 2', onTrigger: () => console.log('Effect action 2') },
]);
}, []);
return (
<Box sx={{ padding: 40 }}>
<Button onClick={openSpotlight}>Open spotlight</Button>
<Button
onClick={() =>
registerSpotlightActions([
{ title: 'Registered', onTrigger: () => console.log('registered') },
])
}
>
Register actions
</Button>
</Box>
);
}
function Control() {
const spotlight = useSpotlight();
return (
<Box sx={{ padding: 40 }}>
<Button onClick={() => spotlight.openSpotlight()}>Open spotlight</Button>
<Button
onClick={() =>
spotlight.registerActions([
{ title: 'Registered', onTrigger: () => console.log('registered') },
])
}
>
Register actions
</Button>
</Box>
);
}
function Wrapper(props: Omit<SpotlightProviderProps, 'children'> & { children?: React.ReactNode }) {
return <SpotlightProvider {...props}>{props.children || <Control />}</SpotlightProvider>;
}
function CustomActionComponent() {
return (
<SpotlightProvider
actions={[
{ title: 'Action 1', extra: 'Extra info 1', onTrigger: () => console.log('Action 1') },
{ title: 'Action 2', extra: 'Extra info 2', onTrigger: () => console.log('Action 2') },
]}
actionComponent={({ onTrigger, hovered, action, ...others }) => (
<button type="button" style={{ background: hovered ? 'red' : undefined }} {...others}>
{action.extra}
</button>
)}
>
<Control />
</SpotlightProvider>
);
}
function DynamicActions() {
return (
<SpotlightProvider
nothingFoundMessage="Nothing found"
placeholder="Dynamic actions"
actions={(query) =>
query.trim().length > 0
? [
{ title: `Search docs: ${query}`, onTrigger: () => console.log('Search') },
{ title: `Create new ticket: ${query}`, onTrigger: () => console.log('Search') },
]
: []
}
>
<Control />
</SpotlightProvider>
);
}
const defaultProps: Omit<SpotlightProviderProps, 'children'> = {
actions: DEFAULT_ACTIONS,
searchPlaceholder: 'Search...',
nothingFoundMessage: 'Nothing found...',
};
storiesOf('Spotlight', module)
.add('Default', () => <Wrapper {...defaultProps} />)
.add('Centered', () => <Wrapper {...defaultProps} centered />)
.add('With search icon', () => (
<Wrapper {...defaultProps} searchIcon={<IconSearch size={18} />} />
))
.add('With action icon', () => (
<Wrapper
{...defaultProps}
searchIcon={<IconSearch size={18} />}
actions={[
{ title: 'Search', icon: <IconSearch size={18} />, onTrigger: () => console.log('Search') },
{
title: 'Search',
description: 'Search action with description',
icon: <IconSearch size={18} />,
onTrigger: () => console.log('Search'),
},
{
title: 'Search',
description:
'Action description that may collapse to next line and may break the icon, bu who know how it will turn out',
icon: <IconSearch size={18} />,
onTrigger: () => console.log('Search'),
},
{
title:
'Action title that will overflow to next line and may collapse the icon or maybe not, who knows',
description: 'Search action with description',
icon: <IconSearch size={18} />,
onTrigger: () => console.log('Search'),
},
{
title: 'Icon that has really huge icon',
description: 'This is user fault, will not be handled on library side',
icon: <IconSearch size={100} />,
onTrigger: () => console.log('Search'),
},
]}
/>
))
.add('Custom filter', () => (
<Wrapper
{...defaultProps}
filter={(query, actions) => actions.filter((action) => action.title.includes(query))}
/>
))
.add('Limit', () => <Wrapper {...defaultProps} actions={LARGE_ACTIONS_SET} limit={5} />)
.add('Dynamic actions', () => <DynamicActions />)
.add('closeOnActionTrigger: false', () => (
<Wrapper {...defaultProps} closeOnActionTrigger={false} />
))
.add('Custom action component', () => <CustomActionComponent />)
.add('Custom wrapper component', () => (
<Wrapper
{...defaultProps}
actionsWrapperComponent={({ children }) => (
<div>
<div>
<button type="button">Header button 1</button>
<button type="button">Header button 2</button>
</div>
{children}
<div>
<button type="button">Footer button 1</button>
<button type="button">Footer button 2</button>
</div>
</div>
)}
/>
))
.add('Multiple shortcuts', () => (
<Wrapper {...defaultProps} shortcut={['mod + K', 'mod + P', 'mod + /']} />
))
.add('Grouped actions', () => (
<Wrapper
{...defaultProps}
actions={[
{ title: 'Create 1', group: 'Create', onTrigger: () => console.log('Crate') },
{ title: 'Search 1', group: 'Search', onTrigger: () => console.log('Search') },
{ title: 'No group', onTrigger: () => console.log('No Group') },
{ title: 'Create 2', group: 'Create', onTrigger: () => console.log('Crate') },
{ title: 'Search 2', group: 'Search', onTrigger: () => console.log('Search') },
{ title: 'Search 3', group: 'Search', onTrigger: () => console.log('Search') },
{ title: 'Create 3', group: 'Create', onTrigger: () => console.log('Crate') },
]}
/>
))
.add('Highlight query', () => <Wrapper {...defaultProps} highlightQuery />)
.add('Highlight with custom color', () => (
<Wrapper {...defaultProps} highlightColor="red" highlightQuery />
))
.add('Register in useEffect', () => (
<Wrapper {...defaultProps}>
<RegisterInEffect />
</Wrapper>
))
.add('Actions with closeOnTrigger', () => (
<Wrapper
{...defaultProps}
actions={[
{
title: 'Should stay open',
onTrigger: () => console.log('Should stay open'),
closeOnTrigger: false,
},
{
title: 'Should close',
onTrigger: () => console.log('Should close'),
closeOnTrigger: true,
},
]}
/>
));