dropdowns-js 1.0.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/README.md ADDED
@@ -0,0 +1,478 @@
1
+ # dropdowns-js
2
+
3
+ ## Overview
4
+ Searchable dropdown components for React applications.
5
+ - Dropdown - a single selection dropdown whose underlying data is primitive type array.
6
+ - DropdownObj - a single selection dropdown whose underlying data is an array of objects.
7
+ - MultiSelectionDropdown - a multi-selection dropdown whose underlying data is a primitive array.
8
+ - MultiSelectionDropdownObj - a multi-selection dropdown whose underlying data is an array of objects.
9
+
10
+ ## Installation
11
+ ```
12
+ npm install dropdowns-js
13
+ ```
14
+
15
+ ## 1. Wrap your App
16
+ All components must be wrapped in the CollectionsProvider to manage the dropdown states.
17
+
18
+ ```
19
+ // App.jsx
20
+ import { CollectionsProvider } from "dropdowns-js";
21
+ import MyComponent from "./MyComponent";
22
+
23
+ function App() {
24
+ return (
25
+ <CollectionsProvider>
26
+ <MyComponent />
27
+ </CollectionsProvider>
28
+ );
29
+ }
30
+ export default App;
31
+ ```
32
+
33
+ ## 2. Import styles and hook
34
+ Inside your component, where you use any of the dropdowns, import as follows:
35
+
36
+ ```
37
+ // File: MyComponent.jsx
38
+ import "dropdowns-js/style.css"; // Must not be left out, so as to enforce dropdown styling.
39
+ import {
40
+ useCollectionsContext,
41
+ Dropdown, // If you use it.
42
+ DropdownObj, // If you use it.
43
+ MultiselectionDropdown, // If you use it.
44
+ MultiselectionDropdownObj, // If you use it.
45
+ } from "dropdowns-js";
46
+
47
+ export default function MyComponent {
48
+ // Inside your component.
49
+ const {
50
+ addCollection, // A must. For pre-populating your dropdown
51
+ collectionExists, // A must. To be called prior to adding a collection.
52
+ getCollectionData, // Depends
53
+ updateCollection, // Depends on whether the collection's data gets updated.
54
+ setSelected, // Depends. Use-case: pre-populated selection.
55
+ getSelected // A must. For obtaining selected items.
56
+ } = useCollectionsContext();
57
+
58
+ // ...
59
+ }
60
+ ```
61
+ **Context functions**
62
+ `addCollection(collectionName, anArray, maxNumSelections = null, primitiveType = true, ...sortFields)`
63
+ Add a new collection of data to be displayed in a dropdown for selection.
64
+
65
+
66
+ `collectionExists(collectionName)`
67
+ Check (true/false) whether the collection with the specified name exists.
68
+
69
+ `updateCollection(collectionName, anArray)`
70
+ Update the specified collection with new data. An error is thrown for a non-existent collection name. A dropdown whose data is updated must be given a key attribute, and the key value must be updated to cause a re-render with new data.
71
+
72
+ `getCollectionData(collectionName)`
73
+ Get the data (array) of the specified collection. An error is thrown for a non-existent collection name.
74
+
75
+ `setSelected(collectionName, selectedItemsArray)`
76
+ Set the selected items in the specified collection. They are ignored if they were are part of the collection. An error is thrown for a non-existent collection name.
77
+
78
+ `getSelected(collectionName)`
79
+ Get the selected items (array) of the specified collection. An error is thrown for a non-existent collection name is specified.
80
+
81
+ `getMaxNumSelections(collectionName)`
82
+ Get the maximum number of items that can be selected on this collection. An error is thrown for a non-existent collection name is specified.
83
+
84
+ ## 3. DropdownStyle and ButtonStyle
85
+ - The DropdownStyle attribute must be provided with an object with field values { color, backgroundColor }.
86
+ - The ButtonStyle attribute must be provided also provided an object with field values { color, backgroundColor }
87
+
88
+ ## 4. Dropdown usage example
89
+ This dropdown is to be used when the underlying data is a primitive type array.
90
+ ```
91
+ import { Dropdown, useCollectionsContext } from 'dropdowns-js';
92
+ import 'dropdowns-js/style.css'; // styles must be imported, otherwise the dropdowns do not display properly.
93
+ import { useEffect, useState } from 'react';
94
+
95
+ export default function MyComponent() {
96
+ const [output, setOutput] = useState('');
97
+
98
+ const {
99
+ addCollection,
100
+ collectionExists,
101
+ getSelected
102
+ } = useCollectionsContext();
103
+
104
+ useEffect(()=> {
105
+ if (!collectionExists("FRUITS")) {
106
+ const fruits = ["APPLE", "BANANA" "ORANGE", "NAARJIE", "PEACH"]
107
+ // Create an APPLES collection
108
+ addCollection("FRUITS", // collection name
109
+ fruits, // collection data
110
+ 1, // Maximum number of allowed selections.
111
+ true, // true = primitive type (string) data collection
112
+ 'asc'); // sort order
113
+ }
114
+ }, []);
115
+
116
+ /**Respond when the user has chosen a fruit */
117
+ function fruitSelected() {
118
+ // Obtain the selected items. Only 1 selection was made (size 1 array)
119
+ const selectedFruit = getSelected("FRUITS")[0];
120
+ setOutput(selectedFruit);
121
+ }
122
+
123
+ return (
124
+ <div className='' style={{padding: '5px', backgroundColor: 'green'}}>
125
+ <h1>Dropdown Example</h1>
126
+ <p>Select a fruit</p>
127
+ <div style={{padding: '2px', display: 'flex'}}>
128
+ <label htmlFor='fruits' style={{width: '70px'}}>Fruit</label>
129
+
130
+ <Dropdown id='fruits' label={'Fruits'} collectionName={'FRUITS'}
131
+ onItemSelected={fruitSelected}
132
+ dropdownStyle={{color: '#000', backgroundColor: '#66ff66'}} />
133
+
134
+ </div>
135
+ <p>{output}</p>
136
+
137
+ </div>
138
+ );
139
+ }
140
+ ```
141
+ ## 5. DropdownObj usage example
142
+ ```
143
+ import { DropdownObj, useCollectionsContext } from 'dropdowns-js';
144
+ import 'dropdowns-js/style.css'; // Not to be left out.
145
+
146
+ import { useEffect, useState } from 'react';
147
+
148
+ export default function MyComponent2() {
149
+ const [output, setOutput] = useState('');
150
+ const [aKey, setAKey] = useState(0);
151
+ const {
152
+ addCollection,
153
+ collectionExists,
154
+ updateCollection,
155
+ getSelected
156
+ } = useCollectionsContext();
157
+
158
+ useEffect(()=> {
159
+ if (!collectionExists("DRIVING_CODES")) {
160
+ // Create a DRIVING_CODES collection, sorted by description ascending
161
+ const drivingCodes = [
162
+ { code: 'A1', description: 'Light Motorcycles' },
163
+ { code: 'A', description: 'Heavy Motorcycles' },
164
+ { code: 'B', description: 'Light Vehicles' },
165
+ { code: 'EB', description: 'Light Articulated' },
166
+ { code: 'C1', description: 'Heavy Vehicles' },
167
+ { code: 'C', description: 'Extra Heavy Vehicles' },
168
+ { code: 'EC1', description: 'Heavy Articulated' },
169
+ { code: 'EC', description: 'Extra Heavy Articulated' }
170
+ ];
171
+ addCollection("DRIVING_CODES", // collection name
172
+ drivingCodes, // collection data
173
+ 1, // allow maximum 1 selection
174
+ false, // non-primitive type, that is, object type data collection
175
+ 'description asc'); // sort order
176
+ }
177
+ }, []);
178
+
179
+ /**Respond when the user has chosen a driving code */
180
+ function drivingCodeSelected() {
181
+ // Obtain the selected items. Only 1 selection was made (size 1 array)
182
+ const selectedDrivingCode = [...getSelected(collectionNames.DRIVING_CODES)[0]];
183
+ setOutput(`${selectedDrivingCode.code} => ${selectedDrivingCode.description}`);
184
+ }
185
+
186
+ return (
187
+ <div className='w3-container' style={{padding: '5px', backgroundColor: 'green'}}>
188
+ <h1>DropdownObj Example</h1>
189
+ <p>Select driving licence code</p>
190
+ <div style={{padding: '2px', display: 'flex'}}>
191
+ <label htmlFor='driving-codes' style={{width: '70px'}}>Driving Codes</label>
192
+
193
+ <DropdownObj id='driving-codes' label='Driving Codes' collectionName={"DRIVING_CODES"}
194
+ displayName="description" valueName="code" onItemSelected={drivingCodeSelected}
195
+ dropdownStyle={{color: '#000', backgroundColor: '#66ff66'}} />
196
+ </div>
197
+
198
+ <p>{output}</p>
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+ ## 6. MultiselectionDropdown usage example
204
+ ```
205
+ import { MultiSelectionDropdown, useCollectionsContext } from 'dropdowns-js';
206
+ import 'dropdowns-js/style.css';
207
+
208
+ import { useEffect, useState } from 'react';
209
+
210
+ export default function MyComponent3() {
211
+ const [output, setOutput] = useState('');
212
+ const [aKey, setAKey] = useState(0);
213
+ const {
214
+ addCollection,
215
+ collectionExists,
216
+ updateCollection,
217
+ getSelected
218
+ } = useCollectionsContext();
219
+
220
+ useEffect(()=> {
221
+ if (!collectionExists("SPORTS")) {
222
+ // Create an SPORT collection sorted in ascending order.
223
+ const sport = [
224
+ "Motor Racing", "Cycling", "Wrestling", "Kung Fu", "Boxing", "Basket Ball",
225
+ "Rugby", "Cricket", "Running", "Soccer", "Netball", "Hockey"
226
+ ];
227
+ addCollection("SPORT", // collection name
228
+ sport, // collection data
229
+ 4, // maximum number of selections
230
+ true, // primitive type data (string in this case)
231
+ 'asc'); // asc - sort order
232
+ }
233
+ }, []);
234
+
235
+ /**Respond when the user has chosen an interest */
236
+ function sportSelected() {
237
+ // Obtain the selected items.
238
+ const selectedSport = getSelected("SPORT").join(", );
239
+ setOutput(selectedSport);
240
+ }
241
+
242
+ return (
243
+ <div className='container' style={{padding: '5px', backgroundColor: 'green'}}>
244
+ <h1>MultiSelectionDropdown Example</h1>
245
+ <p>Select an interest, and then your topic</p>
246
+ <div style={{padding: '2px', display: 'flex'}}>
247
+ <label htmlFor='sport' style={{width: '70px'}}>Sport</label>
248
+
249
+ <MultiSelectionDropdown label='Sport'
250
+ id='sport'
251
+ collectionName={"SPORT"}
252
+ onItemsSelected={sportSelected}
253
+ dropdownStyle={{color: '#000', backgroundColor: '#66ff66'}}
254
+ buttonStyle={{color: '#fff', backgroundColor: '#008000'}} />
255
+ </div>
256
+
257
+ <>{output}</>
258
+ </div>
259
+ );
260
+ }
261
+ ```
262
+
263
+ ## 7. MultiSelectionDropdownObj usage example
264
+ ```
265
+ import 'dropdowns-js/style.css';
266
+ import { MultiSelectionDropdownObj, useCollectionsContext } from 'dropdowns-js';
267
+
268
+ import { useEffect, useState } from 'react';
269
+
270
+
271
+ export default function MyComponent4() {
272
+ const [output, setOutput] = useState('');
273
+ const [dropdownKey2, setDropdownKey2] = useState(0);
274
+ const keyStep = .000001;
275
+ const {
276
+ addCollection,
277
+ collectionExists,
278
+ updateCollection,
279
+ getSelected
280
+ } = useCollectionsContext();
281
+
282
+ const collectionNames = {
283
+ DRIVING_CODES: "DRIVING_CODES",
284
+ DRIVERS: "DRIVERS"
285
+ };
286
+
287
+ useEffect(()=> {
288
+ if (!collectionExists(collectionNames.DRIVING_CODES)) {
289
+ // Create DRIVING_CODES collection sorted in ascending order.
290
+ // Create a DRIVING_CODES collection, sorted by description ascending
291
+ const drivingCodes = [
292
+ { code: 'A1', description: 'Light Motorcycles' },
293
+ { code: 'A', description: 'Heavy Motorcycles' },
294
+ { code: 'B', description: 'Light Vehicles' },
295
+ { code: 'EB', description: 'Light Articulated' },
296
+ { code: 'C1', description: 'Heavy Vehicles' },
297
+ { code: 'C', description: 'Extra Heavy Vehicles' },
298
+ { code: 'EC1', description: 'Heavy Articulated' },
299
+ { code: 'EC', description: 'Extra Heavy Articulated' }
300
+ ];
301
+
302
+ addCollection("DRIVING_CODES", // collection name
303
+ drivingCodes, // collection data
304
+ 3, // maximum number of selections
305
+ false, // object type data
306
+ 'description asc'); // asc - sort order
307
+ }
308
+ }, []);
309
+
310
+ /**Respond when the user has chosen an interest */
311
+ function drivingCodesSelected() {
312
+ // Obtain the selected driving codes.
313
+ const selectedDrivingCodes = getSelected("DRIVING_CODES");
314
+
315
+ // Create a string array of driving codes.
316
+ const strSelectedCodes = selectedDrivingCodes.map((drivingCode)=> drivingCode.code)
317
+ .map(drivingCode => drivingCode.code)
318
+ .join(", ");
319
+ setOutput(strSelectedCodes);
320
+ }
321
+
322
+ return (
323
+ <div className='container' style={{padding: '5px', backgroundColor: 'green'}}>
324
+ <h1>MultiSelectionDropdownObj Example</h1>
325
+ <p>Select an interest, and then your topic</p>
326
+ <div style={{padding: '2px', display: 'flex'}}>
327
+ <label htmlFor={'driving-licence-codes'} style={{width: '100px'}}>Lic. Codes</label>
328
+ <MultiSelectionDropdownObj
329
+ id='driving-licence-codes'
330
+ label='Driving Licence Codes'
331
+ collectionName='DRIVING_CODES'
332
+ valueName='code'
333
+ displayName='description'
334
+ onItemsSelected={drivingCodeSelected}
335
+ isDisabled={false}
336
+ dropdownStyle={{color: '#000', backgroundColor: '#66ff66'}}
337
+ buttonStyle={{color: '#fff', backgroundColor: '#008000'}} />
338
+ </div>
339
+
340
+ {(output.length > 0) &&
341
+ <div style={{ marginTop: '10px', padding: '5px' }}>
342
+ {output}
343
+ </div>
344
+ }
345
+
346
+ </div>
347
+ );
348
+ }
349
+ ```
350
+ ### 8. Multiple dropdown usage example
351
+ ```
352
+ export default function MyComponent5() {
353
+ const [output, setOutput] = useState();
354
+ /* A dropdown whose collection data gets updated, must be given a key attribute, so as to update
355
+ it (key) to cause re-render upon the update of collection data. */
356
+ const [dropdownKey2, setDropdownKey2] = useState(0);
357
+ const keyStep = .000001;
358
+
359
+ const {
360
+ addCollection,
361
+ collectionExists,
362
+ updateCollection,
363
+ getSelected
364
+ } = useCollectionsContext();
365
+
366
+ const drivers = [
367
+ { fullName: "Thabo Mokoena", licenceCode: "B", id: "771217" },
368
+ { fullName: "Naledi Khumalo", licenceCode: "EB", id: "850412" },
369
+ { fullName: "Sipho Dlamini", licenceCode: "C1", id: "861802" },
370
+ { fullName: "Ayesha Patel", licenceCode: "B", id: "500101" },
371
+ { fullName: "Johan van der Merwe", licenceCode: "EC", id: "620401" },
372
+ { fullName: "Lerato Molefe", licenceCode: "A", id: "901012" },
373
+ { fullName: "Sibusiso Nkosi", licenceCode: "EC1", id: "910512" },
374
+ { fullName: "Michelle Adams", licenceCode: "B", id: "901012" },
375
+ { fullName: "Kagiso Tshepe", licenceCode: "C", id: "920115" },
376
+ { fullName: "Pieter Botha", licenceCode: "A1", id: "960401" },
377
+ { fullName: "Nomvula Zungu", licenceCode: "B", id: "0792346789" },
378
+ { fullName: "Riaan Pretorius", licenceCode: "EB", id: "0824567812" },
379
+ { fullName: "Mandla Sithole", licenceCode: "C1", id: "0735678910" },
380
+ { fullName: "Fatima Khan", licenceCode: "B", id: "0846789123" },
381
+ { fullName: "Tshepo Madiba", licenceCode: "EC1", id: "0817890234" },
382
+ { fullName: "Andre Nel", licenceCode: "A", id: "0728901345" },
383
+ { fullName: "Zanele Mthembu", licenceCode: "B", id: "0769012456" },
384
+ { fullName: "Imran Desai", licenceCode: "C", id: "0740123567" },
385
+ { fullName: "Charlene Jacobs", licenceCode: "EB", id: "0831234678" },
386
+ { fullName: "Vusi Hlongwane", licenceCode: "EC", id: "0712345789" }
387
+ ];
388
+
389
+ useEffect(()=> {
390
+ if (!collectionExists("DRIVING_CODES")) {
391
+ const drivingCodes = [
392
+ "A", "A1", "EC", "EC1", "C", "C1", "B", "EB"
393
+ ];
394
+ // Create DRIVING_CODES collection sorted in ascending order.
395
+ addCollection("DRIVING_CODES", // collection name
396
+ drivingCodes, // collection data
397
+ 1, // maximum number of selections
398
+ true, // primitive type data
399
+ 'asc'); // asc - sort order
400
+ }
401
+ if (!collectionExists("DRIVERS")) {
402
+ // Create a drivers collection sorted in ascending order. Initially empty pending the selection of a driving code.
403
+
404
+ addCollection("DRIVERS",
405
+ [], // empty
406
+ 5, // allow max 5 selections
407
+ false, // object type data.
408
+ 'fullName asc');
409
+ }
410
+ }, []);
411
+
412
+ /**Respond when the user has chosen a driving licence code */
413
+ function drivingCodeSelected() {
414
+ // Obtain the selected driving codes.
415
+ const selectedDrivingCodes = getSelected("DRIVING_CODES");
416
+
417
+ // Create a string array of driving codes.
418
+ const strSelectedCodes = selectedDrivingCodes; // Expecting a size 1 array
419
+ const selectedCode = strSelectedCodes[0];
420
+
421
+ /*=========Update collection according to selected driving licence code=====*/
422
+ const updateData = drivers.filter(driver=> {
423
+ return selectedCode === driver.licenceCode;
424
+ });
425
+ updateCollection("DRIVERS", updateData);
426
+ /*============================================================================*/
427
+
428
+ // Force the re-render the drivers MultiselectionDropdownObj, since its data has been updated.
429
+ setDropdownKey2(dropdownKey2 + keyStep);
430
+ }
431
+
432
+ function driversSelected() {
433
+ const selectedDrivers = [...getSelected(collectionNames.DRIVERS)].map(driver => driver.fullName); // Array
434
+ setOutput(selectedDrivers.join(', ')); // List of selected drivers' names.
435
+ }
436
+
437
+ return (
438
+ <div className='container' style={{padding: '5px', backgroundColor: 'green'}}>
439
+ <h1>MultiSelectionDropdownObj Example</h1>
440
+ <p>Select an interest, and then your topic</p>
441
+ <div style={{padding: '2px', display: 'flex'}}>
442
+ <label style={{width: '100px'}}>Lic. Codes</label>
443
+ <MultiSelectionDropdownObj label='Driving Licence Codes'
444
+ collectionName={collectionNames.DRIVING_CODES}
445
+ valueName='code'
446
+ displayName='description'
447
+ onItemsSelected={drivingCodeSelected}
448
+ isDisabled={false}
449
+ dropdownStyle={{color: '#000', backgroundColor: '#66ff66'}}
450
+ buttonStyle={{color: '#fff', backgroundColor: '#008000'}} />
451
+ </div>
452
+
453
+ <div style={{padding: '2px', display: 'flex'}}>
454
+ <label style={{width: '100px'}}>Drivers</label>
455
+ <MultiSelectionDropdownObj
456
+ key={dropdownKey2}
457
+ collectionName={collectionNames.DRIVERS}
458
+ label='Drivers'
459
+ valueName='id'
460
+ displayName='fullName'
461
+ onItemsSelected={driversSelected}
462
+ dropdownStyle={{color: '#000', backgroundColor: '#66ff66'}}
463
+ buttonStyle={{color: '#fff', backgroundColor: '#008000'}} />
464
+ </div>
465
+
466
+ {output &&
467
+ <div style={{ marginTop: '10px', padding: '5px' }}>
468
+ {output}
469
+ </div>
470
+ }
471
+
472
+ </div>
473
+ );
474
+ }
475
+ ```
476
+
477
+ ## License
478
+ MIT