├── .babelrc ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .npmignore ├── README.md ├── data └── schema.graphql ├── package.json ├── src ├── components │ ├── DataContainer.js │ ├── DataList.js │ ├── LoadMore.js │ └── Table.js ├── index.js └── utils │ └── props.js ├── storybook ├── config.js ├── relay │ └── environment.js └── stories │ ├── Checkboxes.story.js │ ├── DataList.story.js │ └── Table.story.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["flow", "es2015", "react", "stage-0"], 3 | "plugins": [ 4 | ["relay", {"schema": "data/schema.graphql"}], 5 | ], 6 | } 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "parser": "babel-eslint", 4 | "env": { 5 | "browser": true, 6 | "jest": true 7 | }, 8 | "settings": { 9 | "import/resolver": "webpack" 10 | }, 11 | "rules": { 12 | "arrow-parens": 0, 13 | "react/jsx-filename-extension": 0, 14 | "import/extensions": 0, 15 | "max-len": [1, 120, 4, {"ignoreRegExpLiterals": true}], 16 | "jsx-a11y/no-static-element-interactions": 0, 17 | "react/no-unescaped-entities": 0, 18 | "react/forbid-prop-types": 0, 19 | "react/jsx-wrap-multilines": 0, 20 | "no-use-before-define": 0, 21 | "react/prop-types": 0, 22 | "react/jsx-boolean-value": 0, 23 | "default-case": 0, 24 | "react/no-children-prop": 0, 25 | "jsx-a11y/img-has-alt": 0, 26 | "comma-dangle": 0, 27 | "no-return-assign": 0, 28 | "consistent-return": 0, 29 | "class-methods-use-this": 0, 30 | "import/prefer-default-export": 0, 31 | "react/no-did-mount-set-state": 0, 32 | "prefer-const": [ 2, { "destructuring": "all" }], 33 | "react/prefer-stateless-function": [2, { "ignorePureComponents": true }] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | 7 | [options] 8 | sharedmemory.hash_table_pow=21 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | .idea 5 | storybook-static 6 | __generated__/ 7 | lib 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | .idea 5 | storybook-static 6 | __generated__/ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DataList 2 | 3 | ## Install 4 | 5 | ``` 6 | npm i @entria/datalist --save 7 | yarn add @entria/datalist 8 | ``` 9 | 10 | ## Usage 11 | 12 | ```js 13 | user.active ? 'Yes' : 'No', 40 | }, 41 | ], 42 | }} 43 | checkboxes={{ 44 | component: (props) => , 45 | onChange: (values) => console.log(values), 46 | }} 47 | /> 48 | ``` 49 | 50 | ### Props 51 | 52 | - **environment**: The Relay Environment 53 | - **query**: The Relay query 54 | - **fragments**: An object with the query fragments 55 | - **variables**: An object with the query default variables 56 | - **table**: An object with table configs 57 | - **checkboxes**: An object with checkboxes configs 58 | -------------------------------------------------------------------------------- /data/schema.graphql: -------------------------------------------------------------------------------- 1 | schema { 2 | query: Root 3 | } 4 | 5 | # A single film. 6 | type Film implements Node { 7 | # The title of this film. 8 | title: String 9 | 10 | # The episode number of this film. 11 | episodeID: Int 12 | 13 | # The opening paragraphs at the beginning of this film. 14 | openingCrawl: String 15 | 16 | # The name of the director of this film. 17 | director: String 18 | 19 | # The name(s) of the producer(s) of this film. 20 | producers: [String] 21 | 22 | # The ISO 8601 date format of film release at original creator country. 23 | releaseDate: String 24 | speciesConnection(after: String, first: Int, before: String, last: Int): FilmSpeciesConnection 25 | starshipConnection(after: String, first: Int, before: String, last: Int): FilmStarshipsConnection 26 | vehicleConnection(after: String, first: Int, before: String, last: Int): FilmVehiclesConnection 27 | characterConnection(after: String, first: Int, before: String, last: Int): FilmCharactersConnection 28 | planetConnection(after: String, first: Int, before: String, last: Int): FilmPlanetsConnection 29 | 30 | # The ISO 8601 date format of the time that this resource was created. 31 | created: String 32 | 33 | # The ISO 8601 date format of the time that this resource was edited. 34 | edited: String 35 | 36 | # The ID of an object 37 | id: ID! 38 | } 39 | 40 | # A connection to a list of items. 41 | type FilmCharactersConnection { 42 | # Information to aid in pagination. 43 | pageInfo: PageInfo! 44 | 45 | # A list of edges. 46 | edges: [FilmCharactersEdge] 47 | 48 | # A count of the total number of objects in this connection, ignoring pagination. 49 | # This allows a client to fetch the first five objects by passing "5" as the 50 | # argument to "first", then fetch the total count so it could display "5 of 83", 51 | # for example. 52 | totalCount: Int 53 | 54 | # A list of all of the objects returned in the connection. This is a convenience 55 | # field provided for quickly exploring the API; rather than querying for 56 | # "{ edges { node } }" when no edge data is needed, this field can be be used 57 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 58 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 59 | # full "{ edges { node } }" version should be used instead. 60 | characters: [Person] 61 | } 62 | 63 | # An edge in a connection. 64 | type FilmCharactersEdge { 65 | # The item at the end of the edge 66 | node: Person 67 | 68 | # A cursor for use in pagination 69 | cursor: String! 70 | } 71 | 72 | # A connection to a list of items. 73 | type FilmPlanetsConnection { 74 | # Information to aid in pagination. 75 | pageInfo: PageInfo! 76 | 77 | # A list of edges. 78 | edges: [FilmPlanetsEdge] 79 | 80 | # A count of the total number of objects in this connection, ignoring pagination. 81 | # This allows a client to fetch the first five objects by passing "5" as the 82 | # argument to "first", then fetch the total count so it could display "5 of 83", 83 | # for example. 84 | totalCount: Int 85 | 86 | # A list of all of the objects returned in the connection. This is a convenience 87 | # field provided for quickly exploring the API; rather than querying for 88 | # "{ edges { node } }" when no edge data is needed, this field can be be used 89 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 90 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 91 | # full "{ edges { node } }" version should be used instead. 92 | planets: [Planet] 93 | } 94 | 95 | # An edge in a connection. 96 | type FilmPlanetsEdge { 97 | # The item at the end of the edge 98 | node: Planet 99 | 100 | # A cursor for use in pagination 101 | cursor: String! 102 | } 103 | 104 | # A connection to a list of items. 105 | type FilmsConnection { 106 | # Information to aid in pagination. 107 | pageInfo: PageInfo! 108 | 109 | # A list of edges. 110 | edges: [FilmsEdge] 111 | 112 | # A count of the total number of objects in this connection, ignoring pagination. 113 | # This allows a client to fetch the first five objects by passing "5" as the 114 | # argument to "first", then fetch the total count so it could display "5 of 83", 115 | # for example. 116 | totalCount: Int 117 | 118 | # A list of all of the objects returned in the connection. This is a convenience 119 | # field provided for quickly exploring the API; rather than querying for 120 | # "{ edges { node } }" when no edge data is needed, this field can be be used 121 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 122 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 123 | # full "{ edges { node } }" version should be used instead. 124 | films: [Film] 125 | } 126 | 127 | # An edge in a connection. 128 | type FilmsEdge { 129 | # The item at the end of the edge 130 | node: Film 131 | 132 | # A cursor for use in pagination 133 | cursor: String! 134 | } 135 | 136 | # A connection to a list of items. 137 | type FilmSpeciesConnection { 138 | # Information to aid in pagination. 139 | pageInfo: PageInfo! 140 | 141 | # A list of edges. 142 | edges: [FilmSpeciesEdge] 143 | 144 | # A count of the total number of objects in this connection, ignoring pagination. 145 | # This allows a client to fetch the first five objects by passing "5" as the 146 | # argument to "first", then fetch the total count so it could display "5 of 83", 147 | # for example. 148 | totalCount: Int 149 | 150 | # A list of all of the objects returned in the connection. This is a convenience 151 | # field provided for quickly exploring the API; rather than querying for 152 | # "{ edges { node } }" when no edge data is needed, this field can be be used 153 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 154 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 155 | # full "{ edges { node } }" version should be used instead. 156 | species: [Species] 157 | } 158 | 159 | # An edge in a connection. 160 | type FilmSpeciesEdge { 161 | # The item at the end of the edge 162 | node: Species 163 | 164 | # A cursor for use in pagination 165 | cursor: String! 166 | } 167 | 168 | # A connection to a list of items. 169 | type FilmStarshipsConnection { 170 | # Information to aid in pagination. 171 | pageInfo: PageInfo! 172 | 173 | # A list of edges. 174 | edges: [FilmStarshipsEdge] 175 | 176 | # A count of the total number of objects in this connection, ignoring pagination. 177 | # This allows a client to fetch the first five objects by passing "5" as the 178 | # argument to "first", then fetch the total count so it could display "5 of 83", 179 | # for example. 180 | totalCount: Int 181 | 182 | # A list of all of the objects returned in the connection. This is a convenience 183 | # field provided for quickly exploring the API; rather than querying for 184 | # "{ edges { node } }" when no edge data is needed, this field can be be used 185 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 186 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 187 | # full "{ edges { node } }" version should be used instead. 188 | starships: [Starship] 189 | } 190 | 191 | # An edge in a connection. 192 | type FilmStarshipsEdge { 193 | # The item at the end of the edge 194 | node: Starship 195 | 196 | # A cursor for use in pagination 197 | cursor: String! 198 | } 199 | 200 | # A connection to a list of items. 201 | type FilmVehiclesConnection { 202 | # Information to aid in pagination. 203 | pageInfo: PageInfo! 204 | 205 | # A list of edges. 206 | edges: [FilmVehiclesEdge] 207 | 208 | # A count of the total number of objects in this connection, ignoring pagination. 209 | # This allows a client to fetch the first five objects by passing "5" as the 210 | # argument to "first", then fetch the total count so it could display "5 of 83", 211 | # for example. 212 | totalCount: Int 213 | 214 | # A list of all of the objects returned in the connection. This is a convenience 215 | # field provided for quickly exploring the API; rather than querying for 216 | # "{ edges { node } }" when no edge data is needed, this field can be be used 217 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 218 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 219 | # full "{ edges { node } }" version should be used instead. 220 | vehicles: [Vehicle] 221 | } 222 | 223 | # An edge in a connection. 224 | type FilmVehiclesEdge { 225 | # The item at the end of the edge 226 | node: Vehicle 227 | 228 | # A cursor for use in pagination 229 | cursor: String! 230 | } 231 | 232 | # An object with an ID 233 | interface Node { 234 | # The id of the object. 235 | id: ID! 236 | } 237 | 238 | # Information about pagination in a connection. 239 | type PageInfo { 240 | # When paginating forwards, are there more items? 241 | hasNextPage: Boolean! 242 | 243 | # When paginating backwards, are there more items? 244 | hasPreviousPage: Boolean! 245 | 246 | # When paginating backwards, the cursor to continue. 247 | startCursor: String 248 | 249 | # When paginating forwards, the cursor to continue. 250 | endCursor: String 251 | } 252 | 253 | # A connection to a list of items. 254 | type PeopleConnection { 255 | # Information to aid in pagination. 256 | pageInfo: PageInfo! 257 | 258 | # A list of edges. 259 | edges: [PeopleEdge] 260 | 261 | # A count of the total number of objects in this connection, ignoring pagination. 262 | # This allows a client to fetch the first five objects by passing "5" as the 263 | # argument to "first", then fetch the total count so it could display "5 of 83", 264 | # for example. 265 | totalCount: Int 266 | 267 | # A list of all of the objects returned in the connection. This is a convenience 268 | # field provided for quickly exploring the API; rather than querying for 269 | # "{ edges { node } }" when no edge data is needed, this field can be be used 270 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 271 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 272 | # full "{ edges { node } }" version should be used instead. 273 | people: [Person] 274 | } 275 | 276 | # An edge in a connection. 277 | type PeopleEdge { 278 | # The item at the end of the edge 279 | node: Person 280 | 281 | # A cursor for use in pagination 282 | cursor: String! 283 | } 284 | 285 | # An individual person or character within the Star Wars universe. 286 | type Person implements Node { 287 | # The name of this person. 288 | name: String 289 | 290 | # The birth year of the person, using the in-universe standard of BBY or ABY - 291 | # Before the Battle of Yavin or After the Battle of Yavin. The Battle of Yavin is 292 | # a battle that occurs at the end of Star Wars episode IV: A New Hope. 293 | birthYear: String 294 | 295 | # The eye color of this person. Will be "unknown" if not known or "n/a" if the 296 | # person does not have an eye. 297 | eyeColor: String 298 | 299 | # The gender of this person. Either "Male", "Female" or "unknown", 300 | # "n/a" if the person does not have a gender. 301 | gender: String 302 | 303 | # The hair color of this person. Will be "unknown" if not known or "n/a" if the 304 | # person does not have hair. 305 | hairColor: String 306 | 307 | # The height of the person in centimeters. 308 | height: Int 309 | 310 | # The mass of the person in kilograms. 311 | mass: Float 312 | 313 | # The skin color of this person. 314 | skinColor: String 315 | 316 | # A planet that this person was born on or inhabits. 317 | homeworld: Planet 318 | filmConnection(after: String, first: Int, before: String, last: Int): PersonFilmsConnection 319 | 320 | # The species that this person belongs to, or null if unknown. 321 | species: Species 322 | starshipConnection(after: String, first: Int, before: String, last: Int): PersonStarshipsConnection 323 | vehicleConnection(after: String, first: Int, before: String, last: Int): PersonVehiclesConnection 324 | 325 | # The ISO 8601 date format of the time that this resource was created. 326 | created: String 327 | 328 | # The ISO 8601 date format of the time that this resource was edited. 329 | edited: String 330 | 331 | # The ID of an object 332 | id: ID! 333 | } 334 | 335 | # A connection to a list of items. 336 | type PersonFilmsConnection { 337 | # Information to aid in pagination. 338 | pageInfo: PageInfo! 339 | 340 | # A list of edges. 341 | edges: [PersonFilmsEdge] 342 | 343 | # A count of the total number of objects in this connection, ignoring pagination. 344 | # This allows a client to fetch the first five objects by passing "5" as the 345 | # argument to "first", then fetch the total count so it could display "5 of 83", 346 | # for example. 347 | totalCount: Int 348 | 349 | # A list of all of the objects returned in the connection. This is a convenience 350 | # field provided for quickly exploring the API; rather than querying for 351 | # "{ edges { node } }" when no edge data is needed, this field can be be used 352 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 353 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 354 | # full "{ edges { node } }" version should be used instead. 355 | films: [Film] 356 | } 357 | 358 | # An edge in a connection. 359 | type PersonFilmsEdge { 360 | # The item at the end of the edge 361 | node: Film 362 | 363 | # A cursor for use in pagination 364 | cursor: String! 365 | } 366 | 367 | # A connection to a list of items. 368 | type PersonStarshipsConnection { 369 | # Information to aid in pagination. 370 | pageInfo: PageInfo! 371 | 372 | # A list of edges. 373 | edges: [PersonStarshipsEdge] 374 | 375 | # A count of the total number of objects in this connection, ignoring pagination. 376 | # This allows a client to fetch the first five objects by passing "5" as the 377 | # argument to "first", then fetch the total count so it could display "5 of 83", 378 | # for example. 379 | totalCount: Int 380 | 381 | # A list of all of the objects returned in the connection. This is a convenience 382 | # field provided for quickly exploring the API; rather than querying for 383 | # "{ edges { node } }" when no edge data is needed, this field can be be used 384 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 385 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 386 | # full "{ edges { node } }" version should be used instead. 387 | starships: [Starship] 388 | } 389 | 390 | # An edge in a connection. 391 | type PersonStarshipsEdge { 392 | # The item at the end of the edge 393 | node: Starship 394 | 395 | # A cursor for use in pagination 396 | cursor: String! 397 | } 398 | 399 | # A connection to a list of items. 400 | type PersonVehiclesConnection { 401 | # Information to aid in pagination. 402 | pageInfo: PageInfo! 403 | 404 | # A list of edges. 405 | edges: [PersonVehiclesEdge] 406 | 407 | # A count of the total number of objects in this connection, ignoring pagination. 408 | # This allows a client to fetch the first five objects by passing "5" as the 409 | # argument to "first", then fetch the total count so it could display "5 of 83", 410 | # for example. 411 | totalCount: Int 412 | 413 | # A list of all of the objects returned in the connection. This is a convenience 414 | # field provided for quickly exploring the API; rather than querying for 415 | # "{ edges { node } }" when no edge data is needed, this field can be be used 416 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 417 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 418 | # full "{ edges { node } }" version should be used instead. 419 | vehicles: [Vehicle] 420 | } 421 | 422 | # An edge in a connection. 423 | type PersonVehiclesEdge { 424 | # The item at the end of the edge 425 | node: Vehicle 426 | 427 | # A cursor for use in pagination 428 | cursor: String! 429 | } 430 | 431 | # A large mass, planet or planetoid in the Star Wars Universe, at the time of 432 | # 0 ABY. 433 | type Planet implements Node { 434 | # The name of this planet. 435 | name: String 436 | 437 | # The diameter of this planet in kilometers. 438 | diameter: Int 439 | 440 | # The number of standard hours it takes for this planet to complete a single 441 | # rotation on its axis. 442 | rotationPeriod: Int 443 | 444 | # The number of standard days it takes for this planet to complete a single orbit 445 | # of its local star. 446 | orbitalPeriod: Int 447 | 448 | # A number denoting the gravity of this planet, where "1" is normal or 1 standard 449 | # G. "2" is twice or 2 standard Gs. "0.5" is half or 0.5 standard Gs. 450 | gravity: String 451 | 452 | # The average population of sentient beings inhabiting this planet. 453 | population: Float 454 | 455 | # The climates of this planet. 456 | climates: [String] 457 | 458 | # The terrains of this planet. 459 | terrains: [String] 460 | 461 | # The percentage of the planet surface that is naturally occuring water or bodies 462 | # of water. 463 | surfaceWater: Float 464 | residentConnection(after: String, first: Int, before: String, last: Int): PlanetResidentsConnection 465 | filmConnection(after: String, first: Int, before: String, last: Int): PlanetFilmsConnection 466 | 467 | # The ISO 8601 date format of the time that this resource was created. 468 | created: String 469 | 470 | # The ISO 8601 date format of the time that this resource was edited. 471 | edited: String 472 | 473 | # The ID of an object 474 | id: ID! 475 | } 476 | 477 | # A connection to a list of items. 478 | type PlanetFilmsConnection { 479 | # Information to aid in pagination. 480 | pageInfo: PageInfo! 481 | 482 | # A list of edges. 483 | edges: [PlanetFilmsEdge] 484 | 485 | # A count of the total number of objects in this connection, ignoring pagination. 486 | # This allows a client to fetch the first five objects by passing "5" as the 487 | # argument to "first", then fetch the total count so it could display "5 of 83", 488 | # for example. 489 | totalCount: Int 490 | 491 | # A list of all of the objects returned in the connection. This is a convenience 492 | # field provided for quickly exploring the API; rather than querying for 493 | # "{ edges { node } }" when no edge data is needed, this field can be be used 494 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 495 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 496 | # full "{ edges { node } }" version should be used instead. 497 | films: [Film] 498 | } 499 | 500 | # An edge in a connection. 501 | type PlanetFilmsEdge { 502 | # The item at the end of the edge 503 | node: Film 504 | 505 | # A cursor for use in pagination 506 | cursor: String! 507 | } 508 | 509 | # A connection to a list of items. 510 | type PlanetResidentsConnection { 511 | # Information to aid in pagination. 512 | pageInfo: PageInfo! 513 | 514 | # A list of edges. 515 | edges: [PlanetResidentsEdge] 516 | 517 | # A count of the total number of objects in this connection, ignoring pagination. 518 | # This allows a client to fetch the first five objects by passing "5" as the 519 | # argument to "first", then fetch the total count so it could display "5 of 83", 520 | # for example. 521 | totalCount: Int 522 | 523 | # A list of all of the objects returned in the connection. This is a convenience 524 | # field provided for quickly exploring the API; rather than querying for 525 | # "{ edges { node } }" when no edge data is needed, this field can be be used 526 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 527 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 528 | # full "{ edges { node } }" version should be used instead. 529 | residents: [Person] 530 | } 531 | 532 | # An edge in a connection. 533 | type PlanetResidentsEdge { 534 | # The item at the end of the edge 535 | node: Person 536 | 537 | # A cursor for use in pagination 538 | cursor: String! 539 | } 540 | 541 | # A connection to a list of items. 542 | type PlanetsConnection { 543 | # Information to aid in pagination. 544 | pageInfo: PageInfo! 545 | 546 | # A list of edges. 547 | edges: [PlanetsEdge] 548 | 549 | # A count of the total number of objects in this connection, ignoring pagination. 550 | # This allows a client to fetch the first five objects by passing "5" as the 551 | # argument to "first", then fetch the total count so it could display "5 of 83", 552 | # for example. 553 | totalCount: Int 554 | 555 | # A list of all of the objects returned in the connection. This is a convenience 556 | # field provided for quickly exploring the API; rather than querying for 557 | # "{ edges { node } }" when no edge data is needed, this field can be be used 558 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 559 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 560 | # full "{ edges { node } }" version should be used instead. 561 | planets: [Planet] 562 | } 563 | 564 | # An edge in a connection. 565 | type PlanetsEdge { 566 | # The item at the end of the edge 567 | node: Planet 568 | 569 | # A cursor for use in pagination 570 | cursor: String! 571 | } 572 | 573 | type Root { 574 | allFilms(after: String, first: Int, before: String, last: Int): FilmsConnection 575 | film(id: ID, filmID: ID): Film 576 | allPeople(after: String, first: Int, before: String, last: Int): PeopleConnection 577 | person(id: ID, personID: ID): Person 578 | allPlanets(after: String, first: Int, before: String, last: Int): PlanetsConnection 579 | planet(id: ID, planetID: ID): Planet 580 | allSpecies(after: String, first: Int, before: String, last: Int): SpeciesConnection 581 | species(id: ID, speciesID: ID): Species 582 | allStarships(after: String, first: Int, before: String, last: Int): StarshipsConnection 583 | starship(id: ID, starshipID: ID): Starship 584 | allVehicles(after: String, first: Int, before: String, last: Int): VehiclesConnection 585 | vehicle(id: ID, vehicleID: ID): Vehicle 586 | 587 | # Fetches an object given its ID 588 | node( 589 | # The ID of an object 590 | id: ID! 591 | ): Node 592 | } 593 | 594 | # A type of person or character within the Star Wars Universe. 595 | type Species implements Node { 596 | # The name of this species. 597 | name: String 598 | 599 | # The classification of this species, such as "mammal" or "reptile". 600 | classification: String 601 | 602 | # The designation of this species, such as "sentient". 603 | designation: String 604 | 605 | # The average height of this species in centimeters. 606 | averageHeight: Float 607 | 608 | # The average lifespan of this species in years, null if unknown. 609 | averageLifespan: Int 610 | 611 | # Common eye colors for this species, null if this species does not typically 612 | # have eyes. 613 | eyeColors: [String] 614 | 615 | # Common hair colors for this species, null if this species does not typically 616 | # have hair. 617 | hairColors: [String] 618 | 619 | # Common skin colors for this species, null if this species does not typically 620 | # have skin. 621 | skinColors: [String] 622 | 623 | # The language commonly spoken by this species. 624 | language: String 625 | 626 | # A planet that this species originates from. 627 | homeworld: Planet 628 | personConnection(after: String, first: Int, before: String, last: Int): SpeciesPeopleConnection 629 | filmConnection(after: String, first: Int, before: String, last: Int): SpeciesFilmsConnection 630 | 631 | # The ISO 8601 date format of the time that this resource was created. 632 | created: String 633 | 634 | # The ISO 8601 date format of the time that this resource was edited. 635 | edited: String 636 | 637 | # The ID of an object 638 | id: ID! 639 | } 640 | 641 | # A connection to a list of items. 642 | type SpeciesConnection { 643 | # Information to aid in pagination. 644 | pageInfo: PageInfo! 645 | 646 | # A list of edges. 647 | edges: [SpeciesEdge] 648 | 649 | # A count of the total number of objects in this connection, ignoring pagination. 650 | # This allows a client to fetch the first five objects by passing "5" as the 651 | # argument to "first", then fetch the total count so it could display "5 of 83", 652 | # for example. 653 | totalCount: Int 654 | 655 | # A list of all of the objects returned in the connection. This is a convenience 656 | # field provided for quickly exploring the API; rather than querying for 657 | # "{ edges { node } }" when no edge data is needed, this field can be be used 658 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 659 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 660 | # full "{ edges { node } }" version should be used instead. 661 | species: [Species] 662 | } 663 | 664 | # An edge in a connection. 665 | type SpeciesEdge { 666 | # The item at the end of the edge 667 | node: Species 668 | 669 | # A cursor for use in pagination 670 | cursor: String! 671 | } 672 | 673 | # A connection to a list of items. 674 | type SpeciesFilmsConnection { 675 | # Information to aid in pagination. 676 | pageInfo: PageInfo! 677 | 678 | # A list of edges. 679 | edges: [SpeciesFilmsEdge] 680 | 681 | # A count of the total number of objects in this connection, ignoring pagination. 682 | # This allows a client to fetch the first five objects by passing "5" as the 683 | # argument to "first", then fetch the total count so it could display "5 of 83", 684 | # for example. 685 | totalCount: Int 686 | 687 | # A list of all of the objects returned in the connection. This is a convenience 688 | # field provided for quickly exploring the API; rather than querying for 689 | # "{ edges { node } }" when no edge data is needed, this field can be be used 690 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 691 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 692 | # full "{ edges { node } }" version should be used instead. 693 | films: [Film] 694 | } 695 | 696 | # An edge in a connection. 697 | type SpeciesFilmsEdge { 698 | # The item at the end of the edge 699 | node: Film 700 | 701 | # A cursor for use in pagination 702 | cursor: String! 703 | } 704 | 705 | # A connection to a list of items. 706 | type SpeciesPeopleConnection { 707 | # Information to aid in pagination. 708 | pageInfo: PageInfo! 709 | 710 | # A list of edges. 711 | edges: [SpeciesPeopleEdge] 712 | 713 | # A count of the total number of objects in this connection, ignoring pagination. 714 | # This allows a client to fetch the first five objects by passing "5" as the 715 | # argument to "first", then fetch the total count so it could display "5 of 83", 716 | # for example. 717 | totalCount: Int 718 | 719 | # A list of all of the objects returned in the connection. This is a convenience 720 | # field provided for quickly exploring the API; rather than querying for 721 | # "{ edges { node } }" when no edge data is needed, this field can be be used 722 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 723 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 724 | # full "{ edges { node } }" version should be used instead. 725 | people: [Person] 726 | } 727 | 728 | # An edge in a connection. 729 | type SpeciesPeopleEdge { 730 | # The item at the end of the edge 731 | node: Person 732 | 733 | # A cursor for use in pagination 734 | cursor: String! 735 | } 736 | 737 | # A single transport craft that has hyperdrive capability. 738 | type Starship implements Node { 739 | # The name of this starship. The common name, such as "Death Star". 740 | name: String 741 | 742 | # The model or official name of this starship. Such as "T-65 X-wing" or "DS-1 743 | # Orbital Battle Station". 744 | model: String 745 | 746 | # The class of this starship, such as "Starfighter" or "Deep Space Mobile 747 | # Battlestation" 748 | starshipClass: String 749 | 750 | # The manufacturers of this starship. 751 | manufacturers: [String] 752 | 753 | # The cost of this starship new, in galactic credits. 754 | costInCredits: Float 755 | 756 | # The length of this starship in meters. 757 | length: Float 758 | 759 | # The number of personnel needed to run or pilot this starship. 760 | crew: String 761 | 762 | # The number of non-essential people this starship can transport. 763 | passengers: String 764 | 765 | # The maximum speed of this starship in atmosphere. null if this starship is 766 | # incapable of atmosphering flight. 767 | maxAtmospheringSpeed: Int 768 | 769 | # The class of this starships hyperdrive. 770 | hyperdriveRating: Float 771 | 772 | # The Maximum number of Megalights this starship can travel in a standard hour. 773 | # A "Megalight" is a standard unit of distance and has never been defined before 774 | # within the Star Wars universe. This figure is only really useful for measuring 775 | # the difference in speed of starships. We can assume it is similar to AU, the 776 | # distance between our Sun (Sol) and Earth. 777 | MGLT: Int 778 | 779 | # The maximum number of kilograms that this starship can transport. 780 | cargoCapacity: Float 781 | 782 | # The maximum length of time that this starship can provide consumables for its 783 | # entire crew without having to resupply. 784 | consumables: String 785 | pilotConnection(after: String, first: Int, before: String, last: Int): StarshipPilotsConnection 786 | filmConnection(after: String, first: Int, before: String, last: Int): StarshipFilmsConnection 787 | 788 | # The ISO 8601 date format of the time that this resource was created. 789 | created: String 790 | 791 | # The ISO 8601 date format of the time that this resource was edited. 792 | edited: String 793 | 794 | # The ID of an object 795 | id: ID! 796 | } 797 | 798 | # A connection to a list of items. 799 | type StarshipFilmsConnection { 800 | # Information to aid in pagination. 801 | pageInfo: PageInfo! 802 | 803 | # A list of edges. 804 | edges: [StarshipFilmsEdge] 805 | 806 | # A count of the total number of objects in this connection, ignoring pagination. 807 | # This allows a client to fetch the first five objects by passing "5" as the 808 | # argument to "first", then fetch the total count so it could display "5 of 83", 809 | # for example. 810 | totalCount: Int 811 | 812 | # A list of all of the objects returned in the connection. This is a convenience 813 | # field provided for quickly exploring the API; rather than querying for 814 | # "{ edges { node } }" when no edge data is needed, this field can be be used 815 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 816 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 817 | # full "{ edges { node } }" version should be used instead. 818 | films: [Film] 819 | } 820 | 821 | # An edge in a connection. 822 | type StarshipFilmsEdge { 823 | # The item at the end of the edge 824 | node: Film 825 | 826 | # A cursor for use in pagination 827 | cursor: String! 828 | } 829 | 830 | # A connection to a list of items. 831 | type StarshipPilotsConnection { 832 | # Information to aid in pagination. 833 | pageInfo: PageInfo! 834 | 835 | # A list of edges. 836 | edges: [StarshipPilotsEdge] 837 | 838 | # A count of the total number of objects in this connection, ignoring pagination. 839 | # This allows a client to fetch the first five objects by passing "5" as the 840 | # argument to "first", then fetch the total count so it could display "5 of 83", 841 | # for example. 842 | totalCount: Int 843 | 844 | # A list of all of the objects returned in the connection. This is a convenience 845 | # field provided for quickly exploring the API; rather than querying for 846 | # "{ edges { node } }" when no edge data is needed, this field can be be used 847 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 848 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 849 | # full "{ edges { node } }" version should be used instead. 850 | pilots: [Person] 851 | } 852 | 853 | # An edge in a connection. 854 | type StarshipPilotsEdge { 855 | # The item at the end of the edge 856 | node: Person 857 | 858 | # A cursor for use in pagination 859 | cursor: String! 860 | } 861 | 862 | # A connection to a list of items. 863 | type StarshipsConnection { 864 | # Information to aid in pagination. 865 | pageInfo: PageInfo! 866 | 867 | # A list of edges. 868 | edges: [StarshipsEdge] 869 | 870 | # A count of the total number of objects in this connection, ignoring pagination. 871 | # This allows a client to fetch the first five objects by passing "5" as the 872 | # argument to "first", then fetch the total count so it could display "5 of 83", 873 | # for example. 874 | totalCount: Int 875 | 876 | # A list of all of the objects returned in the connection. This is a convenience 877 | # field provided for quickly exploring the API; rather than querying for 878 | # "{ edges { node } }" when no edge data is needed, this field can be be used 879 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 880 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 881 | # full "{ edges { node } }" version should be used instead. 882 | starships: [Starship] 883 | } 884 | 885 | # An edge in a connection. 886 | type StarshipsEdge { 887 | # The item at the end of the edge 888 | node: Starship 889 | 890 | # A cursor for use in pagination 891 | cursor: String! 892 | } 893 | 894 | # A single transport craft that does not have hyperdrive capability 895 | type Vehicle implements Node { 896 | # The name of this vehicle. The common name, such as "Sand Crawler" or "Speeder 897 | # bike". 898 | name: String 899 | 900 | # The model or official name of this vehicle. Such as "All-Terrain Attack 901 | # Transport". 902 | model: String 903 | 904 | # The class of this vehicle, such as "Wheeled" or "Repulsorcraft". 905 | vehicleClass: String 906 | 907 | # The manufacturers of this vehicle. 908 | manufacturers: [String] 909 | 910 | # The cost of this vehicle new, in Galactic Credits. 911 | costInCredits: Int 912 | 913 | # The length of this vehicle in meters. 914 | length: Float 915 | 916 | # The number of personnel needed to run or pilot this vehicle. 917 | crew: String 918 | 919 | # The number of non-essential people this vehicle can transport. 920 | passengers: String 921 | 922 | # The maximum speed of this vehicle in atmosphere. 923 | maxAtmospheringSpeed: Int 924 | 925 | # The maximum number of kilograms that this vehicle can transport. 926 | cargoCapacity: Int 927 | 928 | # The maximum length of time that this vehicle can provide consumables for its 929 | # entire crew without having to resupply. 930 | consumables: String 931 | pilotConnection(after: String, first: Int, before: String, last: Int): VehiclePilotsConnection 932 | filmConnection(after: String, first: Int, before: String, last: Int): VehicleFilmsConnection 933 | 934 | # The ISO 8601 date format of the time that this resource was created. 935 | created: String 936 | 937 | # The ISO 8601 date format of the time that this resource was edited. 938 | edited: String 939 | 940 | # The ID of an object 941 | id: ID! 942 | } 943 | 944 | # A connection to a list of items. 945 | type VehicleFilmsConnection { 946 | # Information to aid in pagination. 947 | pageInfo: PageInfo! 948 | 949 | # A list of edges. 950 | edges: [VehicleFilmsEdge] 951 | 952 | # A count of the total number of objects in this connection, ignoring pagination. 953 | # This allows a client to fetch the first five objects by passing "5" as the 954 | # argument to "first", then fetch the total count so it could display "5 of 83", 955 | # for example. 956 | totalCount: Int 957 | 958 | # A list of all of the objects returned in the connection. This is a convenience 959 | # field provided for quickly exploring the API; rather than querying for 960 | # "{ edges { node } }" when no edge data is needed, this field can be be used 961 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 962 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 963 | # full "{ edges { node } }" version should be used instead. 964 | films: [Film] 965 | } 966 | 967 | # An edge in a connection. 968 | type VehicleFilmsEdge { 969 | # The item at the end of the edge 970 | node: Film 971 | 972 | # A cursor for use in pagination 973 | cursor: String! 974 | } 975 | 976 | # A connection to a list of items. 977 | type VehiclePilotsConnection { 978 | # Information to aid in pagination. 979 | pageInfo: PageInfo! 980 | 981 | # A list of edges. 982 | edges: [VehiclePilotsEdge] 983 | 984 | # A count of the total number of objects in this connection, ignoring pagination. 985 | # This allows a client to fetch the first five objects by passing "5" as the 986 | # argument to "first", then fetch the total count so it could display "5 of 83", 987 | # for example. 988 | totalCount: Int 989 | 990 | # A list of all of the objects returned in the connection. This is a convenience 991 | # field provided for quickly exploring the API; rather than querying for 992 | # "{ edges { node } }" when no edge data is needed, this field can be be used 993 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 994 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 995 | # full "{ edges { node } }" version should be used instead. 996 | pilots: [Person] 997 | } 998 | 999 | # An edge in a connection. 1000 | type VehiclePilotsEdge { 1001 | # The item at the end of the edge 1002 | node: Person 1003 | 1004 | # A cursor for use in pagination 1005 | cursor: String! 1006 | } 1007 | 1008 | # A connection to a list of items. 1009 | type VehiclesConnection { 1010 | # Information to aid in pagination. 1011 | pageInfo: PageInfo! 1012 | 1013 | # A list of edges. 1014 | edges: [VehiclesEdge] 1015 | 1016 | # A count of the total number of objects in this connection, ignoring pagination. 1017 | # This allows a client to fetch the first five objects by passing "5" as the 1018 | # argument to "first", then fetch the total count so it could display "5 of 83", 1019 | # for example. 1020 | totalCount: Int 1021 | 1022 | # A list of all of the objects returned in the connection. This is a convenience 1023 | # field provided for quickly exploring the API; rather than querying for 1024 | # "{ edges { node } }" when no edge data is needed, this field can be be used 1025 | # instead. Note that when clients like Relay need to fetch the "cursor" field on 1026 | # the edge to enable efficient pagination, this shortcut cannot be used, and the 1027 | # full "{ edges { node } }" version should be used instead. 1028 | vehicles: [Vehicle] 1029 | } 1030 | 1031 | # An edge in a connection. 1032 | type VehiclesEdge { 1033 | # The item at the end of the edge 1034 | node: Vehicle 1035 | 1036 | # A cursor for use in pagination 1037 | cursor: String! 1038 | } 1039 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@entria/datalist", 3 | "version": "0.0.9", 4 | "description": "Turns your data into a table with Relay RefetchContainer", 5 | "keywords": [ 6 | "react", 7 | "relay", 8 | "modern", 9 | "relay modern", 10 | "datalist", 11 | "data", 12 | "list", 13 | "entria" 14 | ], 15 | "license": "ISC", 16 | "homepage": "https://github.com/entria/datalist#readme", 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/entria/datalist" 20 | }, 21 | "main": "lib/index.js", 22 | "module": "src/index.js", 23 | "jsnext:main": "src/index.js", 24 | "devDependencies": { 25 | "@entria/relay-utils": "^0.0.11", 26 | "@entria/utils": "^0.0.3", 27 | "@kadira/storybook": "^2.35.3", 28 | "babel-cli": "^6.24.1", 29 | "babel-eslint": "^7.2.3", 30 | "babel-plugin-relay": "^1.2.0", 31 | "babel-preset-es2015": "^6.24.1", 32 | "babel-preset-flow": "^6.23.0", 33 | "babel-preset-react": "^6.24.1", 34 | "babel-preset-stage-0": "^6.24.1", 35 | "eslint": "^3.19.0", 36 | "eslint-config-airbnb": "^15.0.1", 37 | "eslint-import-resolver-webpack": "^0.8.1", 38 | "eslint-plugin-import": "^2.3.0", 39 | "eslint-plugin-jsx-a11y": "^5.0.3", 40 | "eslint-plugin-react": "^7.1.0", 41 | "flow-bin": "^0.53.0", 42 | "gh-pages": "^1.0.0", 43 | "lint-staged": "^4.0.0", 44 | "lodash": "^4.17.4", 45 | "pre-commit": "^1.2.2", 46 | "prettier": "^1.5.3", 47 | "prop-types": "^15.5.10", 48 | "react": "^15.6.1", 49 | "react-dom": "^15.6.1", 50 | "react-relay": "^1.2.0", 51 | "relay-compiler": "^1.2.0" 52 | }, 53 | "peerDependencies": { 54 | "@entria/relay-utils": ">=0.0.10", 55 | "@entria/utils": ">=0.0.1", 56 | "lodash": ">=4.17.4", 57 | "prop-types": ">=15.5.10", 58 | "react": ">=0.14", 59 | "react-dom": ">=0.14", 60 | "react-relay": ">=1.1.0" 61 | }, 62 | "lint-staged": { 63 | "src/**/*.js": [ 64 | "prettier --write --single-quote true --trailing-comma all --print-width 100", 65 | "git add" 66 | ] 67 | }, 68 | "pre-commit": "lint:staged", 69 | "scripts": { 70 | "prepublish": "npm run build", 71 | "build": "babel src -d lib", 72 | "lint": "eslint --ext js src", 73 | "lint:staged": "lint-staged", 74 | "relay": "relay-compiler --src ./storybook --schema data/schema.graphql", 75 | "relay:watch": "yarn relay -- --watch", 76 | "start": "start-storybook -p 6006 -c storybook", 77 | "demo:build": "build-storybook -c storybook", 78 | "demo:gh-pages": "gh-pages -d storybook-static/", 79 | "demo:publish": "npm run demo:build && npm run demo:gh-pages", 80 | "check": "npm run lint && npm run test", 81 | "release:patch": "npm run check && npm version patch && git push --follow-tags && npm publish --access public", 82 | "release:minor": "npm run check && npm version minor && git push --follow-tags && npm publish --access public", 83 | "release:major": "npm run check && npm version major && git push --follow-tags && npm publish --access public" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/components/DataContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { hasNextPage, createDataArray } from '@entria/relay-utils'; 4 | import { InfiniteScroll } from '@entria/utils'; 5 | 6 | import Table from './Table'; 7 | import LoadMore from './LoadMore'; 8 | 9 | class DataContainer extends Component { 10 | state = { 11 | loading: this.props.loading, 12 | variables: this.props.variables, 13 | count: this.props.variables.first, 14 | checked: this.props.checkboxes.checked || [], 15 | }; 16 | 17 | componentWillReceiveProps(nextProps) { 18 | this.setState({ 19 | loading: nextProps.loading, 20 | }); 21 | } 22 | 23 | fetchMore = (params = {}) => { 24 | const count = params.count || this.state.count; 25 | const { variables } = this.state; 26 | 27 | const options = { 28 | ...variables, 29 | first: variables.first + count, 30 | }; 31 | 32 | this.setState({ 33 | loading: true, 34 | variables: options, 35 | }); 36 | 37 | this.props.relay.refetch(options, null, () => { 38 | this.setState({ 39 | loading: false, 40 | }); 41 | 42 | if (params.onComplete) { 43 | params.onComplete(); 44 | } 45 | }); 46 | }; 47 | 48 | fetchAll = (params = {}) => { 49 | const count = params.count || 100; 50 | const { list } = this.props; 51 | const data = createDataArray(this.props, list); 52 | 53 | if (params.onFetch) { 54 | params.onFetch(data); 55 | } 56 | if (!hasNextPage(this.props) && params.onComplete) { 57 | return params.onComplete(data); 58 | } 59 | 60 | this.fetchMore({ 61 | count, 62 | onComplete: () => this.fetchAll(params), 63 | }); 64 | }; 65 | 66 | checkAll = () => { 67 | this.fetchAll({ 68 | onFetch: data => { 69 | this.setState({ 70 | checked: data, 71 | }); 72 | }, 73 | onComplete: data => { 74 | this.setState({ 75 | checked: data, 76 | }); 77 | 78 | this.props.checkboxes.onChange(data); 79 | }, 80 | }); 81 | }; 82 | 83 | uncheckAll = () => { 84 | const checked = []; 85 | 86 | this.setState({ 87 | checked, 88 | }); 89 | 90 | this.props.checkboxes.onChange(checked); 91 | }; 92 | 93 | check = item => { 94 | const checked = [...this.state.checked, item]; 95 | 96 | this.setState({ 97 | checked, 98 | }); 99 | 100 | this.props.checkboxes.onChange(checked); 101 | }; 102 | 103 | uncheck = item => { 104 | const checked = this.state.checked.filter(selectedItem => selectedItem.id !== item.id); 105 | 106 | this.setState({ 107 | checked, 108 | }); 109 | 110 | this.props.checkboxes.onChange(checked); 111 | }; 112 | 113 | isChecked = item => 114 | this.state.checked.filter(selectedItem => selectedItem.id === item.id).length > 0; 115 | 116 | handleScroll = () => { 117 | const { loading } = this.state; 118 | 119 | if (hasNextPage(this.props) && !loading) { 120 | this.fetchMore(); 121 | } 122 | }; 123 | 124 | prepareProps = () => { 125 | const { table, checkboxes, list } = this.props; 126 | const { loading, checked } = this.state; 127 | const data = createDataArray(this.props, list); 128 | 129 | return { 130 | config: { 131 | table, 132 | checkboxes, 133 | }, 134 | actions: { 135 | checkAll: () => this.checkAll(), 136 | uncheckAll: () => this.uncheckAll(), 137 | check: item => this.check(item), 138 | uncheck: item => this.uncheck(item), 139 | isChecked: item => this.isChecked(item), 140 | }, 141 | loading, 142 | data, 143 | hasNextPage: data.length > 0 && hasNextPage(this.props), 144 | checked, 145 | }; 146 | }; 147 | 148 | render() { 149 | const preparedProps = this.prepareProps(); 150 | 151 | return ( 152 | this.handleScroll()}> 153 |
154 | 155 | 156 | this.fetchMore()} 158 | label={preparedProps.loading ? 'Carregando...' : 'Carregar mais'} 159 | disabled={preparedProps.loading} 160 | visible={preparedProps.hasNextPage} 161 | /> 162 | 163 | 164 | ); 165 | } 166 | } 167 | 168 | DataContainer.defaultProps = { 169 | loading: false, 170 | variables: {}, 171 | table: { 172 | columns: [], 173 | cellRender: null, 174 | }, 175 | checkboxes: { 176 | component: null, 177 | onChange: null, 178 | checked: [], 179 | }, 180 | }; 181 | 182 | DataContainer.propTypes = { 183 | list: PropTypes.string.isRequired, 184 | loading: PropTypes.bool, 185 | variables: PropTypes.object, 186 | table: PropTypes.shape({ 187 | columns: PropTypes.arrayOf( 188 | PropTypes.shape({ 189 | label: PropTypes.string, 190 | property: PropTypes.string, 191 | render: PropTypes.func, 192 | }), 193 | ), 194 | cellRender: PropTypes.func, 195 | }), 196 | checkboxes: PropTypes.shape({ 197 | component: PropTypes.any, 198 | onChange: PropTypes.func, 199 | checked: PropTypes.array, 200 | }), 201 | }; 202 | 203 | export default DataContainer; 204 | -------------------------------------------------------------------------------- /src/components/DataList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { createRefetchContainer, QueryRenderer } from 'react-relay'; 5 | 6 | import { sanitizeVariables } from '../utils/props'; 7 | import DataContainer from './DataContainer'; 8 | 9 | const DataList = props => { 10 | const variables = sanitizeVariables(props.variables); 11 | 12 | const { fragments, query } = props; 13 | const DataListRefetchContainer = createRefetchContainer(DataContainer, fragments, query); 14 | 15 | const { table, checkboxes, list } = props; 16 | return ( 17 | { 22 | if (rendererProps) { 23 | return ( 24 | 32 | ); 33 | } 34 | 35 | return null; 36 | }} 37 | /> 38 | ); 39 | }; 40 | 41 | DataList.defaultProps = { 42 | fragments: {}, 43 | variables: {}, 44 | table: { 45 | columns: [], 46 | cellRender: null, 47 | }, 48 | checkboxes: { 49 | component: null, 50 | onChange: null, 51 | checked: [], 52 | }, 53 | }; 54 | 55 | DataList.propTypes = { 56 | query: PropTypes.func.isRequired, 57 | environment: PropTypes.object.isRequired, 58 | list: PropTypes.string.isRequired, 59 | fragments: PropTypes.object, 60 | variables: PropTypes.object, 61 | table: PropTypes.shape({ 62 | columns: PropTypes.arrayOf( 63 | PropTypes.shape({ 64 | label: PropTypes.string, 65 | property: PropTypes.string, 66 | render: PropTypes.func, 67 | }), 68 | ), 69 | cellRender: PropTypes.func, 70 | }), 71 | checkboxes: PropTypes.shape({ 72 | component: PropTypes.any, 73 | onChange: PropTypes.func, 74 | checked: PropTypes.array, 75 | }), 76 | }; 77 | 78 | export default DataList; 79 | -------------------------------------------------------------------------------- /src/components/LoadMore.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const LoadMore = ({ label, onClick, disabled, visible }) => { 5 | const wrapperStyles = { 6 | ...styles.wrapper, 7 | }; 8 | if (!visible) { 9 | wrapperStyles.opacity = 0; 10 | wrapperStyles.paddingTop = 0; 11 | } 12 | 13 | return ( 14 |
15 | 16 | {label} 17 | 18 |
19 | ); 20 | }; 21 | 22 | const styles = { 23 | wrapper: { 24 | display: 'flex', 25 | justifyContent: 'center', 26 | transition: 'all 0.2s', 27 | opacity: 1, 28 | paddingTop: 20, 29 | }, 30 | button: { 31 | fontSize: 16, 32 | color: '#bababa', 33 | textTransform: 'uppercase', 34 | cursor: 'pointer', 35 | }, 36 | }; 37 | 38 | LoadMore.defaultProps = { 39 | label: 'Carregar mais', 40 | visible: true, 41 | disabled: false, 42 | }; 43 | 44 | LoadMore.propTypes = { 45 | onClick: PropTypes.func.isRequired, 46 | label: PropTypes.string, 47 | visible: PropTypes.bool, 48 | disabled: PropTypes.bool, 49 | }; 50 | 51 | export default LoadMore; 52 | -------------------------------------------------------------------------------- /src/components/Table.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { get } from 'lodash'; 4 | 5 | const Table = ({ config, actions, data, loading, checked }) => { 6 | const { columns, cellRender } = config.table; 7 | const { checkboxes } = config; 8 | 9 | const generateKey = (column, index) => { 10 | const property = column.property || 'noProperty'; 11 | return property + index.toString(); 12 | }; 13 | 14 | const renderHeader = () => 15 |
16 | 17 | {checkboxes.component && 18 | } 31 | {columns.map((column, index) => 32 | , 33 | )} 34 | 35 | ; 36 | 37 | const renderRow = (row, rowIndex) => 38 | 39 | {checkboxes.component && 40 | } 52 | {columns.map((column, colIndex) => { 53 | let cell; 54 | if (column.render) { 55 | cell = column.render(row); 56 | } else { 57 | const value = get(row, column.property); 58 | cell = cellRender ? cellRender(value, row) : value; 59 | } 60 | 61 | return ( 62 | 65 | ); 66 | })} 67 | ; 68 | 69 | const renderMessage = message => { 70 | const length = checkboxes.component ? columns.length + 1 : columns.length; 71 | 72 | return ( 73 | 74 | 77 | 78 | ); 79 | }; 80 | 81 | return ( 82 |
19 | 0 && checked.length >= data.length} 22 | onChange={event => { 23 | if (event.target.checked) { 24 | actions.checkAll(); 25 | } else { 26 | actions.uncheckAll(); 27 | } 28 | }} 29 | /> 30 | {column.label}
41 | { 44 | if (event.target.checked) { 45 | actions.check(row); 46 | } else { 47 | actions.uncheck(row); 48 | } 49 | }} 50 | /> 51 | 63 | {cell} 64 |
75 | {message} 76 |
83 | {renderHeader()} 84 | 85 | 86 | {data.map(renderRow)} 87 | {data.length < 1 && 88 | renderMessage(loading ? 'Carregando...' : 'Nenhum registro encontrado.')} 89 | 90 |
91 | ); 92 | }; 93 | 94 | const styles = { 95 | wrapper: { 96 | width: '100%', 97 | maxWidth: '100%', 98 | borderCollapse: 'collapse', 99 | }, 100 | th: { 101 | padding: '10px 20px', 102 | fontSize: 14, 103 | color: '#bababa', 104 | textTransform: 'uppercase', 105 | textAlign: 'left', 106 | borderBottom: '2px solid #eee', 107 | }, 108 | td: { 109 | padding: 20, 110 | }, 111 | message: { 112 | padding: 20, 113 | fontSize: 14, 114 | color: '#bdbdbd', 115 | textAlign: 'center', 116 | }, 117 | }; 118 | 119 | Table.defaultProps = { 120 | data: [], 121 | loading: false, 122 | }; 123 | 124 | Table.propTypes = { 125 | config: PropTypes.shape({ 126 | table: PropTypes.shape({ 127 | columns: PropTypes.arrayOf( 128 | PropTypes.shape({ 129 | label: PropTypes.string, 130 | property: PropTypes.string, 131 | render: PropTypes.func, 132 | }), 133 | ), 134 | cellRender: PropTypes.func, 135 | }).isRequired, 136 | checkboxes: PropTypes.shape({ 137 | component: PropTypes.any, 138 | onChange: PropTypes.func, 139 | checked: PropTypes.array, 140 | }), 141 | }).isRequired, 142 | actions: PropTypes.shape({ 143 | checkAll: PropTypes.func, 144 | uncheckAll: PropTypes.func, 145 | check: PropTypes.func, 146 | uncheck: PropTypes.func, 147 | }).isRequired, 148 | data: PropTypes.array, 149 | loading: PropTypes.bool, 150 | }; 151 | 152 | export default Table; 153 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import DataList from './components/DataList'; 2 | 3 | export default DataList; 4 | -------------------------------------------------------------------------------- /src/utils/props.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | export const sanitizeVariables = (variables: Object): Object => ({ first: 20, ...variables }); 3 | 4 | export const isLoading = (props: any): boolean => props === null; 5 | -------------------------------------------------------------------------------- /storybook/config.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { configure } from '@kadira/storybook'; 3 | 4 | function loadStories() { 5 | require('./stories/DataList.story'); 6 | require('./stories/Table.story'); 7 | require('./stories/Checkboxes.story'); 8 | } 9 | 10 | configure(loadStories, module); 11 | -------------------------------------------------------------------------------- /storybook/relay/environment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | import { Environment, Network, RecordSource, Store } from 'relay-runtime'; 5 | 6 | const GRAPHQL_URL = process.env.NODE_ENV === 'production' ? 'https://datalist.herokuapp.com/' : 'http://localhost:5000'; 7 | 8 | // Define a function that fetches the results of an operation (query/mutation/etc) 9 | // and returns its results as a Promise: 10 | function fetchQuery(operation, variables, cacheConfig) { 11 | return fetch(GRAPHQL_URL, { 12 | method: 'POST', 13 | headers: { 14 | Accept: 'application/json', 15 | 'Content-Type': 'application/json', 16 | }, // Add authentication and other headers here 17 | body: JSON.stringify({ 18 | query: operation.text, // GraphQL text from input 19 | variables, 20 | }), 21 | }).then(response => { 22 | return response.json(); 23 | }); 24 | } 25 | 26 | // Create a network layer from the fetch function 27 | const network = Network.create(fetchQuery); 28 | 29 | const source = new RecordSource(); 30 | const store = new Store(source); 31 | 32 | const env = new Environment({ 33 | network, 34 | store, 35 | }); 36 | 37 | export default env; 38 | -------------------------------------------------------------------------------- /storybook/stories/Checkboxes.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@kadira/storybook'; 3 | import { graphql } from 'graphql'; 4 | 5 | import DataList from '../../src'; 6 | import Environment from '../relay/environment'; 7 | 8 | const stories = storiesOf('Checkboxes', module); 9 | 10 | stories.add('default', () => 11 | , 45 | onChange: (values) => alert(values.map(value => value.name).join(', ')), 46 | }} 47 | />, 48 | ); 49 | 50 | 51 | stories.add('with checked default', () => 52 | , 86 | onChange: (values) => alert(values.map(value => value.name).join(', ')), 87 | checked: [ 88 | { 89 | id: 'cGxhbmV0czoy', 90 | name: 'Alderaan', 91 | population: 2000000000, 92 | }, 93 | { 94 | id: 'cGxhbmV0czoz', 95 | name: 'Yavin IV', 96 | population: 1000, 97 | }, 98 | { 99 | id: 'cGxhbmV0czo4', 100 | name: 'Naboo', 101 | population: 4500000000, 102 | }, 103 | { 104 | id: 'cGxhbmV0czoxMA==', 105 | name: 'Kamino', 106 | population: 1000000000, 107 | }, 108 | ] 109 | }} 110 | />, 111 | ); 112 | -------------------------------------------------------------------------------- /storybook/stories/DataList.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@kadira/storybook'; 3 | import { graphql } from 'graphql'; 4 | 5 | import DataList from '../../src'; 6 | import Environment from '../relay/environment'; 7 | 8 | const stories = storiesOf('DataList', module); 9 | 10 | stories.add('default', () => 11 | , 43 | ); 44 | -------------------------------------------------------------------------------- /storybook/stories/Table.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@kadira/storybook'; 3 | import { graphql } from 'graphql'; 4 | 5 | import DataList from '../../src'; 6 | import Environment from '../relay/environment'; 7 | 8 | const stories = storiesOf('Table', module); 9 | 10 | stories.add('with column render()', () => 11 | { 44 | const releaseDate = new Date(film.releaseDate); 45 | return releaseDate.getFullYear(); 46 | }, 47 | }, 48 | ], 49 | }} 50 | />, 51 | ); 52 | 53 | stories.add('with cellRender()', () => 54 | ( 86 |
87 | {children} 88 | ({vehicles.vehicleClass}) 89 |
90 | ) 91 | }} 92 | />, 93 | ); 94 | --------------------------------------------------------------------------------