github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/stories/components/table.stories.js (about)

     1  import hbs from 'htmlbars-inline-precompile';
     2  import productMetadata from '../../app/utils/styleguide/product-metadata';
     3  
     4  import EmberObject, { computed } from '@ember/object';
     5  
     6  import { getOwner } from '@ember/application';
     7  import { on } from '@ember/object/evented';
     8  import Controller from '@ember/controller';
     9  
    10  export default {
    11    title: 'Components/Table',
    12  };
    13  
    14  /**
    15   * The Ember integration for Storybook renders a container component with no routing,
    16   * which means things that need query parameters, like sorting and pagination, won’t work.
    17  
    18   * This initialiser turns on routing and accepts a controller definition that gets wired up
    19   * to a generated `storybook` route. The controller is attached to the Storybook component
    20   * as the `controller` property so its query parameters are accessible from the template.
    21   */
    22  function injectRoutedController(controllerClass) {
    23    return on('init', function () {
    24      let container = getOwner(this);
    25      container.register('controller:storybook', controllerClass);
    26  
    27      let routerFactory = container.factoryFor('router:main');
    28      routerFactory.class.map(function () {
    29        this.route('storybook');
    30      });
    31  
    32      /* eslint-disable-next-line ember/no-private-routing-service */
    33      let router = container.lookup('router:main');
    34      router.initialURL = 'storybook';
    35      router.startRouting(true);
    36  
    37      this.set('controller', container.lookup('controller:storybook'));
    38    });
    39  }
    40  
    41  let longList = [
    42    {
    43      city: 'New York',
    44      growth: 0.048,
    45      population: '8405837',
    46      rank: '1',
    47      state: 'New York',
    48    },
    49    {
    50      city: 'Los Angeles',
    51      growth: 0.048,
    52      population: '3884307',
    53      rank: '2',
    54      state: 'California',
    55    },
    56    {
    57      city: 'Chicago',
    58      growth: -0.061,
    59      population: '2718782',
    60      rank: '3',
    61      state: 'Illinois',
    62    },
    63    {
    64      city: 'Houston',
    65      growth: 0.11,
    66      population: '2195914',
    67      rank: '4',
    68      state: 'Texas',
    69    },
    70    {
    71      city: 'Philadelphia',
    72      growth: 0.026,
    73      population: '1553165',
    74      rank: '5',
    75      state: 'Pennsylvania',
    76    },
    77    {
    78      city: 'Phoenix',
    79      growth: 0.14,
    80      population: '1513367',
    81      rank: '6',
    82      state: 'Arizona',
    83    },
    84    {
    85      city: 'San Antonio',
    86      growth: 0.21,
    87      population: '1409019',
    88      rank: '7',
    89      state: 'Texas',
    90    },
    91    {
    92      city: 'San Diego',
    93      growth: 0.105,
    94      population: '1355896',
    95      rank: '8',
    96      state: 'California',
    97    },
    98    {
    99      city: 'Dallas',
   100      growth: 0.056,
   101      population: '1257676',
   102      rank: '9',
   103      state: 'Texas',
   104    },
   105    {
   106      city: 'San Jose',
   107      growth: 0.105,
   108      population: '998537',
   109      rank: '10',
   110      state: 'California',
   111    },
   112    {
   113      city: 'Austin',
   114      growth: 0.317,
   115      population: '885400',
   116      rank: '11',
   117      state: 'Texas',
   118    },
   119    {
   120      city: 'Indianapolis',
   121      growth: 0.078,
   122      population: '843393',
   123      rank: '12',
   124      state: 'Indiana',
   125    },
   126    {
   127      city: 'Jacksonville',
   128      growth: 0.143,
   129      population: '842583',
   130      rank: '13',
   131      state: 'Florida',
   132    },
   133    {
   134      city: 'San Francisco',
   135      growth: 0.077,
   136      population: '837442',
   137      rank: '14',
   138      state: 'California',
   139    },
   140    {
   141      city: 'Columbus',
   142      growth: 0.148,
   143      population: '822553',
   144      rank: '15',
   145      state: 'Ohio',
   146    },
   147    {
   148      city: 'Charlotte',
   149      growth: 0.391,
   150      population: '792862',
   151      rank: '16',
   152      state: 'North Carolina',
   153    },
   154    {
   155      city: 'Fort Worth',
   156      growth: 0.451,
   157      population: '792727',
   158      rank: '17',
   159      state: 'Texas',
   160    },
   161    {
   162      city: 'Detroit',
   163      growth: -0.271,
   164      population: '688701',
   165      rank: '18',
   166      state: 'Michigan',
   167    },
   168    {
   169      city: 'El Paso',
   170      growth: 0.194,
   171      population: '674433',
   172      rank: '19',
   173      state: 'Texas',
   174    },
   175    {
   176      city: 'Memphis',
   177      growth: -0.053,
   178      population: '653450',
   179      rank: '20',
   180      state: 'Tennessee',
   181    },
   182    {
   183      city: 'Seattle',
   184      growth: 0.156,
   185      population: '652405',
   186      rank: '21',
   187      state: 'Washington',
   188    },
   189    {
   190      city: 'Denver',
   191      growth: 0.167,
   192      population: '649495',
   193      rank: '22',
   194      state: 'Colorado',
   195    },
   196    {
   197      city: 'Washington',
   198      growth: 0.13,
   199      population: '646449',
   200      rank: '23',
   201      state: 'District of Columbia',
   202    },
   203    {
   204      city: 'Boston',
   205      growth: 0.094,
   206      population: '645966',
   207      rank: '24',
   208      state: 'Massachusetts',
   209    },
   210    {
   211      city: 'Nashville-Davidson',
   212      growth: 0.162,
   213      population: '634464',
   214      rank: '25',
   215      state: 'Tennessee',
   216    },
   217    {
   218      city: 'Baltimore',
   219      growth: -0.04,
   220      population: '622104',
   221      rank: '26',
   222      state: 'Maryland',
   223    },
   224    {
   225      city: 'Oklahoma City',
   226      growth: 0.202,
   227      population: '610613',
   228      rank: '27',
   229      state: 'Oklahoma',
   230    },
   231    {
   232      city: 'Louisville/Jefferson County',
   233      growth: 0.1,
   234      population: '609893',
   235      rank: '28',
   236      state: 'Kentucky',
   237    },
   238    {
   239      city: 'Portland',
   240      growth: 0.15,
   241      population: '609456',
   242      rank: '29',
   243      state: 'Oregon',
   244    },
   245    {
   246      city: 'Las Vegas',
   247      growth: 0.245,
   248      population: '603488',
   249      rank: '30',
   250      state: 'Nevada',
   251    },
   252    {
   253      city: 'Milwaukee',
   254      growth: 0.003,
   255      population: '599164',
   256      rank: '31',
   257      state: 'Wisconsin',
   258    },
   259    {
   260      city: 'Albuquerque',
   261      growth: 0.235,
   262      population: '556495',
   263      rank: '32',
   264      state: 'New Mexico',
   265    },
   266    {
   267      city: 'Tucson',
   268      growth: 0.075,
   269      population: '526116',
   270      rank: '33',
   271      state: 'Arizona',
   272    },
   273    {
   274      city: 'Fresno',
   275      growth: 0.183,
   276      population: '509924',
   277      rank: '34',
   278      state: 'California',
   279    },
   280    {
   281      city: 'Sacramento',
   282      growth: 0.172,
   283      population: '479686',
   284      rank: '35',
   285      state: 'California',
   286    },
   287    {
   288      city: 'Long Beach',
   289      growth: 0.015,
   290      population: '469428',
   291      rank: '36',
   292      state: 'California',
   293    },
   294    {
   295      city: 'Kansas City',
   296      growth: 0.055,
   297      population: '467007',
   298      rank: '37',
   299      state: 'Missouri',
   300    },
   301    {
   302      city: 'Mesa',
   303      growth: 0.135,
   304      population: '457587',
   305      rank: '38',
   306      state: 'Arizona',
   307    },
   308    {
   309      city: 'Virginia Beach',
   310      growth: 0.051,
   311      population: '448479',
   312      rank: '39',
   313      state: 'Virginia',
   314    },
   315    {
   316      city: 'Atlanta',
   317      growth: 0.062,
   318      population: '447841',
   319      rank: '40',
   320      state: 'Georgia',
   321    },
   322    {
   323      city: 'Colorado Springs',
   324      growth: 0.214,
   325      population: '439886',
   326      rank: '41',
   327      state: 'Colorado',
   328    },
   329    {
   330      city: 'Omaha',
   331      growth: 0.059,
   332      population: '434353',
   333      rank: '42',
   334      state: 'Nebraska',
   335    },
   336    {
   337      city: 'Raleigh',
   338      growth: 0.487,
   339      population: '431746',
   340      rank: '43',
   341      state: 'North Carolina',
   342    },
   343    {
   344      city: 'Miami',
   345      growth: 0.149,
   346      population: '417650',
   347      rank: '44',
   348      state: 'Florida',
   349    },
   350    {
   351      city: 'Oakland',
   352      growth: 0.013,
   353      population: '406253',
   354      rank: '45',
   355      state: 'California',
   356    },
   357    {
   358      city: 'Minneapolis',
   359      growth: 0.045,
   360      population: '400070',
   361      rank: '46',
   362      state: 'Minnesota',
   363    },
   364    {
   365      city: 'Tulsa',
   366      growth: 0.013,
   367      population: '398121',
   368      rank: '47',
   369      state: 'Oklahoma',
   370    },
   371    {
   372      city: 'Cleveland',
   373      growth: -0.181,
   374      population: '390113',
   375      rank: '48',
   376      state: 'Ohio',
   377    },
   378    {
   379      city: 'Wichita',
   380      growth: 0.097,
   381      population: '386552',
   382      rank: '49',
   383      state: 'Kansas',
   384    },
   385    {
   386      city: 'Arlington',
   387      growth: 0.133,
   388      population: '379577',
   389      rank: '50',
   390      state: 'Texas',
   391    },
   392  ];
   393  
   394  export let Standard = () => {
   395    return {
   396      template: hbs`
   397        <h5 class="title is-5">Table</h5>
   398        <ListTable @source={{shortList}} as |t|>
   399          <t.head>
   400            <th>Name</th>
   401            <th>Language</th>
   402            <th>Description</th>
   403          </t.head>
   404          <t.body @key="model.name" as |row|>
   405            <tr>
   406              <td>{{row.model.name}}</td>
   407              <td>{{row.model.lang}}</td>
   408              <td>{{row.model.desc}}</td>
   409            </tr>
   410          </t.body>
   411        </ListTable>
   412        <p class="annotation">Tables have airy designs with a minimal amount of borders. This maximizes their utility.</p>
   413        `,
   414      context: {
   415        shortList: productMetadata,
   416      },
   417    };
   418  };
   419  
   420  export let Search = () => {
   421    return {
   422      template: hbs`
   423        <h5 class="title is-5">Table search</h5>
   424        <div class="boxed-section">
   425          <div class="boxed-section-head">
   426            Table Name
   427            <SearchBox
   428              @searchTerm={{mut controller.searchTerm}}
   429              @placeholder="Search..."
   430              @class="is-inline pull-right"
   431              @inputClass="is-compact" />
   432          </div>
   433          <div class="boxed-section-body {{if controller.filteredShortList.length "is-full-bleed"}}">
   434            {{#if controller.filteredShortList.length}}
   435              <ListTable @source={{controller.filteredShortList}} as |t|>
   436                <t.head>
   437                  <th>Name</th>
   438                  <th>Language</th>
   439                  <th>Description</th>
   440                </t.head>
   441                <t.body @key="model.name" as |row|>
   442                  <tr>
   443                    <td>{{row.model.name}}</td>
   444                    <td>{{row.model.lang}}</td>
   445                    <td>{{row.model.desc}}</td>
   446                  </tr>
   447                </t.body>
   448              </ListTable>
   449            {{else}}
   450              <div class="empty-message">
   451                <h3 class="empty-message-headline">No Matches</h3>
   452                <p class="empty-message-body">No products match your query.</p>
   453              </div>
   454            {{/if}}
   455          </div>
   456        </div>
   457        <p class="annotation">Tables compose with boxed-section and boxed-section composes with search box.</p>
   458        `,
   459      context: {
   460        controller: EmberObject.extend({
   461          searchTerm: '',
   462  
   463          filteredShortList: computed('searchTerm', function () {
   464            let term = this.searchTerm.toLowerCase();
   465            return productMetadata.filter((product) =>
   466              product.name.toLowerCase().includes(term)
   467            );
   468          }),
   469        }).create(),
   470      },
   471    };
   472  };
   473  
   474  export let SortableColumns = () => {
   475    return {
   476      template: hbs`
   477        <h5 class="title is-5">Table with sortable columns</h5>
   478        <ListTable @source={{sortedShortList}} @sortProperty={{controller.sortProperty}} @sortDescending={{controller.sortDescending}} as |t|>
   479          <t.head>
   480            <t.sort-by @prop="name">Name</t.sort-by>
   481            <t.sort-by @prop="lang" @class="is-2">Language</t.sort-by>
   482            <th>Description</th>
   483          </t.head>
   484          <t.body @key="model.name" as |row|>
   485            <tr>
   486              <td>{{row.model.name}}</td>
   487              <td>{{row.model.lang}}</td>
   488              <td>{{row.model.desc}}</td>
   489            </tr>
   490          </t.body>
   491        </ListTable>
   492        <p class="annotation">The list-table component provides a <code>sort-by</code> contextual component for building <code>link-to</code> components with the appropriate query params.</p>
   493        <p class="annotation">This leaves the component stateless, relying on data to be passed down and sending actions back up via the router (via link-to).</p>
   494        `,
   495      context: {
   496        injectRoutedController: injectRoutedController(
   497          Controller.extend({
   498            queryParams: ['sortProperty', 'sortDescending'],
   499            sortProperty: 'name',
   500            sortDescending: false,
   501          })
   502        ),
   503  
   504        sortedShortList: computed(
   505          'controller.{sortProperty,sortDescending}',
   506          function () {
   507            let sorted = productMetadata.sortBy(
   508              this.get('controller.sortProperty') || 'name'
   509            );
   510            return this.get('controller.sortDescending')
   511              ? sorted.reverse()
   512              : sorted;
   513          }
   514        ),
   515      },
   516    };
   517  };
   518  
   519  export let MultiRow = () => {
   520    return {
   521      template: hbs`
   522        <h5 class="title is-5">Multi-row Table</h5>
   523        <ListTable @source={{sortedShortList}} @sortProperty={{controller.sortProperty}} @sortDescending={{controller.sortDescending}} @class="is-striped" as |t|>
   524          <t.head>
   525            <t.sort-by @prop="name">Name</t.sort-by>
   526            <t.sort-by @prop="lang">Language</t.sort-by>
   527          </t.head>
   528          <t.body @key="model.name" as |row|>
   529            <tr>
   530              <td>{{row.model.name}}</td>
   531              <td>{{row.model.lang}}</td>
   532            </tr>
   533            <tr>
   534              <td colspan="2">{{row.model.desc}}</td>
   535            </tr>
   536          </t.body>
   537        </ListTable>
   538        <p class="annotation">The list-table component attempts to be as flexible as possible. For this reason, <code>t.body</code> does not provide the typical <code>tr</code> element. It's sometimes desired to have multiple elements per record.</p>
   539        `,
   540      context: {
   541        injectRoutedController: injectRoutedController(
   542          Controller.extend({
   543            queryParams: ['sortProperty', 'sortDescending'],
   544            sortProperty: 'name',
   545            sortDescending: false,
   546          })
   547        ),
   548  
   549        sortedShortList: computed(
   550          'controller.{sortProperty,sortDescending}',
   551          function () {
   552            let sorted = productMetadata.sortBy(
   553              this.get('controller.sortProperty') || 'name'
   554            );
   555            return this.get('controller.sortDescending')
   556              ? sorted.reverse()
   557              : sorted;
   558          }
   559        ),
   560      },
   561    };
   562  };
   563  
   564  export let Pagination = () => {
   565    return {
   566      template: hbs`
   567        <h5 class="title is-5">Table pagination</h5>
   568        <ListPagination @source={{longList}} @size={{5}} @page={{controller.currentPage}} as |p|>
   569          <ListTable @source={{p.list}} @class="with-foot" as |t|>
   570            <t.head>
   571              <th class="is-1">Rank</th>
   572              <th>City</th>
   573              <th>State</th>
   574              <th>Population</th>
   575              <th>Growth</th>
   576            </t.head>
   577            <t.body @key="model.rank" as |row|>
   578              <tr>
   579                <td>{{row.model.rank}}</td>
   580                <td>{{row.model.city}}</td>
   581                <td>{{row.model.state}}</td>
   582                <td>{{row.model.population}}</td>
   583                <td>{{format-percentage row.model.growth total=1}}</td>
   584              </tr>
   585            </t.body>
   586          </ListTable>
   587          <div class="table-foot">
   588            <nav class="pagination">
   589              <span class="bumper-left">U.S. City population and growth from 2000-2013</span>
   590              <div class="pagination-numbers">
   591                {{p.startsAt}}&ndash;{{p.endsAt}} of {{longList.length}}
   592              </div>
   593                <p.prev @class="pagination-previous"> &lt; </p.prev>
   594                <p.next @class="pagination-next"> &gt; </p.next>
   595                <ul class="pagination-list"></ul>
   596            </nav>
   597          </div>
   598        </ListPagination>
   599        <p class="annotation">Pagination works like sorting: using <code>link-to</code>s to set a query param.</p>
   600        <p class="annotation">Pagination, like Table, is a minimal design. Only a next and previous button are available. The current place in the set of pages is tracked by showing which slice of items is currently shown.</p>
   601        <p class="annotation">The pagination component exposes first and last components (for jumping to the beginning and end of a list) as well as pageLinks for generating links around the current page.</p>
   602        `,
   603      context: {
   604        injectRoutedController: injectRoutedController(
   605          Controller.extend({
   606            queryParams: ['currentPage'],
   607            currentPage: 1,
   608          })
   609        ),
   610        longList,
   611      },
   612    };
   613  };
   614  
   615  export let RowLinks = () => {
   616    return {
   617      template: hbs`
   618        <h5 class="title is-5">Table row links</h5>
   619        <ListTable @source={{shortList}} as |t|>
   620          <t.head>
   621            <th>Name</th>
   622            <th>Language</th>
   623            <th>Description</th>
   624          </t.head>
   625          <t.body @key="model.name" as |row|>
   626            <tr class="is-interactive">
   627              <td><a href="javascript:;" class="is-primary">{{row.model.name}}</a></td>
   628              <td>{{row.model.lang}}</td>
   629              <td>{{row.model.desc}}</td>
   630            </tr>
   631          </t.body>
   632        </ListTable>
   633        <p class="annotation">It is common for tables to act as lists of links, (e.g., clients list all allocations, each row links to the allocation detail). The helper class <code>is-interactive</code> on the <code>tr</code> makes table rows have a pointer cursor. The helper class <code>is-primary</code> on the <code>a</code> element in a table row makes the link bold and black instead of blue. This makes the link stand out less, since the entire row is a link.</p>
   634        <p class="annotation">
   635          A few rules for using table row links:
   636          <ol>
   637            <li>The <code>is-primary</code> cell should always be the first cell</li>
   638            <li>The <code>is-primary</code> cell should always contain a link to the destination in the form of an <code>a</code> element. This is to support opening a link in a new tab.</li>
   639            <li>The full row should transition to the destination on click. This is to improve the usability of a table by creating a larger click area.</li>
   640          </ol>
   641        </p>
   642        `,
   643      context: {
   644        shortList: productMetadata,
   645      },
   646    };
   647  };
   648  
   649  export let CellLinks = () => {
   650    return {
   651      template: hbs`
   652        <h5 class="title is-5">Table cell links</h5>
   653        <ListTable @source={{shortList}} as |t|>
   654          <t.head>
   655            <th>Name</th>
   656            <th>Language</th>
   657            <th>Description</th>
   658          </t.head>
   659          <t.body @key="model.name" as |row|>
   660            <tr>
   661              <td><a href={{row.model.link}} target="_parent">{{row.model.name}}</a></td>
   662              <td>{{row.model.lang}}</td>
   663              <td>{{row.model.desc}}</td>
   664            </tr>
   665          </t.body>
   666        </ListTable>
   667        <p class="annotation">Links in table cells are just links.</p>
   668        `,
   669      context: {
   670        shortList: productMetadata,
   671      },
   672    };
   673  };
   674  
   675  export let CellDecorations = () => {
   676    return {
   677      template: hbs`
   678        <h5 class="title is-5">Table cell decorations</h5>
   679        <ListTable @source={{shortList}} as |t|>
   680          <t.head>
   681            <th>Name</th>
   682            <th>Language</th>
   683            <th>Description</th>
   684          </t.head>
   685          <t.body @key="model.name" as |row|>
   686            <tr>
   687              <td><a href={{row.model.link}}>{{row.model.name}}</a></td>
   688              <td class="nowrap">
   689                <span class="color-swatch
   690                  {{if (eq row.model.lang "ruby") "swatch-6"}}
   691                  {{if (eq row.model.lang "golang") "swatch-5"}}" />
   692                {{row.model.lang}}
   693              </td>
   694              <td>{{row.model.desc}}</td>
   695            </tr>
   696          </t.body>
   697        </ListTable>
   698        <p class="annotation">Small icons and accents of color make tables easier to scan.</p>
   699        `,
   700      context: {
   701        shortList: productMetadata,
   702      },
   703    };
   704  };
   705  
   706  export let CellIcons = () => {
   707    return {
   708      template: hbs`
   709        <h5 class="title is-5">Table cell icons</h5>
   710        <ListPagination @source={{longList}} @size={{5}} @page={{controller.currentPage}} as |p|>
   711          <ListTable @source={{p.list}} @class="with-foot" as |t|>
   712            <t.head>
   713              <th class="is-narrow"></th>
   714              <th class="is-1">Rank</th>
   715              <th>City</th>
   716              <th>State</th>
   717              <th>Population</th>
   718              <th>Growth</th>
   719            </t.head>
   720            <t.body @key="model.rank" as |row|>
   721              <tr>
   722                <td class="is-narrow">
   723                  {{#if (lt row.model.growth 0)}}
   724                    {{x-icon "alert-triangle" class="is-warning"}}
   725                  {{/if}}
   726                </td>
   727                <td>{{row.model.rank}}</td>
   728                <td>{{row.model.city}}</td>
   729                <td>{{row.model.state}}</td>
   730                <td>{{row.model.population}}</td>
   731                <td>{{format-percentage row.model.growth total=1}}</td>
   732              </tr>
   733            </t.body>
   734          </ListTable>
   735          <div class="table-foot">
   736            <nav class="pagination">
   737              <span class="bumper-left">U.S. City population and growth from 2000-2013. Cities with negative growth denoted.</span>
   738              <div class="pagination-numbers">
   739                {{p.startsAt}}&ndash;{{p.endsAt}} of {{longList.length}}
   740              </div>
   741                <p.prev @class="pagination-previous"> &lt; </p.prev>
   742                <p.next @class="pagination-next"> &gt; </p.next>
   743                <ul class="pagination-list"></ul>
   744            </nav>
   745          </div>
   746        </ListPagination>
   747        `,
   748      context: {
   749        injectRoutedController: injectRoutedController(
   750          Controller.extend({
   751            queryParams: ['currentPage'],
   752            currentPage: 1,
   753          })
   754        ),
   755        longList,
   756      },
   757    };
   758  };