import hbs from 'htmlbars-inline-precompile'; import productMetadata from '../../app/utils/styleguide/product-metadata'; import EmberObject, { computed } from '@ember/object'; import { getOwner } from '@ember/application'; import { on } from '@ember/object/evented'; import Controller from '@ember/controller'; export default { title: 'Components|Table', }; /** * The Ember integration for Storybook renders a container component with no routing, * which means things that need query parameters, like sorting and pagination, won’t work. * This initialiser turns on routing and accepts a controller definition that gets wired up * to a generated `storybook` route. The controller is attached to the Storybook component * as the `controller` property so its query parameters are accessible from the template. */ function injectRoutedController(controllerClass) { return on('init', function() { let container = getOwner(this); container.register('controller:storybook', controllerClass); let routerFactory = container.factoryFor('router:main'); routerFactory.class.map(function() { this.route('storybook'); }); let router = container.lookup('router:main'); router.initialURL = 'storybook'; router.startRouting(true); this.set('controller', container.lookup('controller:storybook')); }); } let longList = [ { city: 'New York', growth: 0.048, population: '8405837', rank: '1', state: 'New York' }, { city: 'Los Angeles', growth: 0.048, population: '3884307', rank: '2', state: 'California' }, { city: 'Chicago', growth: -0.061, population: '2718782', rank: '3', state: 'Illinois' }, { city: 'Houston', growth: 0.11, population: '2195914', rank: '4', state: 'Texas' }, { city: 'Philadelphia', growth: 0.026, population: '1553165', rank: '5', state: 'Pennsylvania', }, { city: 'Phoenix', growth: 0.14, population: '1513367', rank: '6', state: 'Arizona' }, { city: 'San Antonio', growth: 0.21, population: '1409019', rank: '7', state: 'Texas' }, { city: 'San Diego', growth: 0.105, population: '1355896', rank: '8', state: 'California' }, { city: 'Dallas', growth: 0.056, population: '1257676', rank: '9', state: 'Texas' }, { city: 'San Jose', growth: 0.105, population: '998537', rank: '10', state: 'California' }, { city: 'Austin', growth: 0.317, population: '885400', rank: '11', state: 'Texas' }, { city: 'Indianapolis', growth: 0.078, population: '843393', rank: '12', state: 'Indiana' }, { city: 'Jacksonville', growth: 0.143, population: '842583', rank: '13', state: 'Florida' }, { city: 'San Francisco', growth: 0.077, population: '837442', rank: '14', state: 'California', }, { city: 'Columbus', growth: 0.148, population: '822553', rank: '15', state: 'Ohio' }, { city: 'Charlotte', growth: 0.391, population: '792862', rank: '16', state: 'North Carolina', }, { city: 'Fort Worth', growth: 0.451, population: '792727', rank: '17', state: 'Texas' }, { city: 'Detroit', growth: -0.271, population: '688701', rank: '18', state: 'Michigan' }, { city: 'El Paso', growth: 0.194, population: '674433', rank: '19', state: 'Texas' }, { city: 'Memphis', growth: -0.053, population: '653450', rank: '20', state: 'Tennessee' }, { city: 'Seattle', growth: 0.156, population: '652405', rank: '21', state: 'Washington' }, { city: 'Denver', growth: 0.167, population: '649495', rank: '22', state: 'Colorado' }, { city: 'Washington', growth: 0.13, population: '646449', rank: '23', state: 'District of Columbia', }, { city: 'Boston', growth: 0.094, population: '645966', rank: '24', state: 'Massachusetts' }, { city: 'Nashville-Davidson', growth: 0.162, population: '634464', rank: '25', state: 'Tennessee', }, { city: 'Baltimore', growth: -0.04, population: '622104', rank: '26', state: 'Maryland' }, { city: 'Oklahoma City', growth: 0.202, population: '610613', rank: '27', state: 'Oklahoma' }, { city: 'Louisville/Jefferson County', growth: 0.1, population: '609893', rank: '28', state: 'Kentucky', }, { city: 'Portland', growth: 0.15, population: '609456', rank: '29', state: 'Oregon' }, { city: 'Las Vegas', growth: 0.245, population: '603488', rank: '30', state: 'Nevada' }, { city: 'Milwaukee', growth: 0.003, population: '599164', rank: '31', state: 'Wisconsin' }, { city: 'Albuquerque', growth: 0.235, population: '556495', rank: '32', state: 'New Mexico' }, { city: 'Tucson', growth: 0.075, population: '526116', rank: '33', state: 'Arizona' }, { city: 'Fresno', growth: 0.183, population: '509924', rank: '34', state: 'California' }, { city: 'Sacramento', growth: 0.172, population: '479686', rank: '35', state: 'California' }, { city: 'Long Beach', growth: 0.015, population: '469428', rank: '36', state: 'California' }, { city: 'Kansas City', growth: 0.055, population: '467007', rank: '37', state: 'Missouri' }, { city: 'Mesa', growth: 0.135, population: '457587', rank: '38', state: 'Arizona' }, { city: 'Virginia Beach', growth: 0.051, population: '448479', rank: '39', state: 'Virginia' }, { city: 'Atlanta', growth: 0.062, population: '447841', rank: '40', state: 'Georgia' }, { city: 'Colorado Springs', growth: 0.214, population: '439886', rank: '41', state: 'Colorado', }, { city: 'Omaha', growth: 0.059, population: '434353', rank: '42', state: 'Nebraska' }, { city: 'Raleigh', growth: 0.487, population: '431746', rank: '43', state: 'North Carolina' }, { city: 'Miami', growth: 0.149, population: '417650', rank: '44', state: 'Florida' }, { city: 'Oakland', growth: 0.013, population: '406253', rank: '45', state: 'California' }, { city: 'Minneapolis', growth: 0.045, population: '400070', rank: '46', state: 'Minnesota' }, { city: 'Tulsa', growth: 0.013, population: '398121', rank: '47', state: 'Oklahoma' }, { city: 'Cleveland', growth: -0.181, population: '390113', rank: '48', state: 'Ohio' }, { city: 'Wichita', growth: 0.097, population: '386552', rank: '49', state: 'Kansas' }, { city: 'Arlington', growth: 0.133, population: '379577', rank: '50', state: 'Texas' }, ]; export let Standard = () => { return { template: hbs`
Table
Name Language Description {{row.model.name}} {{row.model.lang}} {{row.model.desc}}

Tables have airy designs with a minimal amount of borders. This maximizes their utility.

`, context: { shortList: productMetadata, }, }; }; export let Search = () => { return { template: hbs`
Table search
Table Name
{{#if controller.filteredShortList.length}} Name Language Description {{row.model.name}} {{row.model.lang}} {{row.model.desc}} {{else}}

No Matches

No products match your query.

{{/if}}

Tables compose with boxed-section and boxed-section composes with search box.

`, context: { controller: EmberObject.extend({ searchTerm: '', filteredShortList: computed('searchTerm', function() { let term = this.searchTerm.toLowerCase(); return productMetadata.filter(product => product.name.toLowerCase().includes(term)); }), }).create(), }, }; }; export let SortableColumns = () => { return { template: hbs`
Table with sortable columns
Name Language Description {{row.model.name}} {{row.model.lang}} {{row.model.desc}}

The list-table component provides a sort-by contextual component for building link-to components with the appropriate query params.

This leaves the component stateless, relying on data to be passed down and sending actions back up via the router (via link-to).

`, context: { injectRoutedController: injectRoutedController( Controller.extend({ queryParams: ['sortProperty', 'sortDescending'], sortProperty: 'name', sortDescending: false, }) ), sortedShortList: computed('controller.sortProperty', 'controller.sortDescending', function() { let sorted = productMetadata.sortBy(this.get('controller.sortProperty') || 'name'); return this.get('controller.sortDescending') ? sorted.reverse() : sorted; }), }, }; }; export let MultiRow = () => { return { template: hbs`
Multi-row Table
Name Language {{row.model.name}} {{row.model.lang}} {{row.model.desc}}

The list-table component attempts to be as flexible as possible. For this reason, t.body does not provide the typical tr element. It's sometimes desired to have multiple elements per record.

`, context: { injectRoutedController: injectRoutedController( Controller.extend({ queryParams: ['sortProperty', 'sortDescending'], sortProperty: 'name', sortDescending: false, }) ), sortedShortList: computed('controller.sortProperty', 'controller.sortDescending', function() { let sorted = productMetadata.sortBy(this.get('controller.sortProperty') || 'name'); return this.get('controller.sortDescending') ? sorted.reverse() : sorted; }), }, }; }; export let Pagination = () => { return { template: hbs`
Table pagination
Rank City State Population Growth {{row.model.rank}} {{row.model.city}} {{row.model.state}} {{row.model.population}} {{format-percentage row.model.growth total=1}}

Pagination works like sorting: using link-tos to set a query param.

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.

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.

`, context: { injectRoutedController: injectRoutedController( Controller.extend({ queryParams: ['currentPage'], currentPage: 1, }) ), longList, }, }; }; export let RowLinks = () => { return { template: hbs`
Table row links
Name Language Description {{row.model.name}} {{row.model.lang}} {{row.model.desc}}

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 is-interactive on the tr makes table rows have a pointer cursor. The helper class is-primary on the a 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.

A few rules for using table row links:

  1. The is-primary cell should always be the first cell
  2. The is-primary cell should always contain a link to the destination in the form of an a element. This is to support opening a link in a new tab.
  3. 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.

`, context: { shortList: productMetadata, }, }; }; export let CellLinks = () => { return { template: hbs`
Table cell links
Name Language Description {{row.model.name}} {{row.model.lang}} {{row.model.desc}}

Links in table cells are just links.

`, context: { shortList: productMetadata, }, }; }; export let CellDecorations = () => { return { template: hbs`
Table cell decorations
Name Language Description {{row.model.name}} {{row.model.lang}} {{row.model.desc}}

Small icons and accents of color make tables easier to scan.

`, context: { shortList: productMetadata, }, }; }; export let CellIcons = () => { return { template: hbs`
Table cell icons
Rank City State Population Growth {{#if (lt row.model.growth 0)}} {{x-icon "warning" class="is-warning"}} {{/if}} {{row.model.rank}} {{row.model.city}} {{row.model.state}} {{row.model.population}} {{format-percentage row.model.growth total=1}}
`, context: { injectRoutedController: injectRoutedController( Controller.extend({ queryParams: ['currentPage'], currentPage: 1, }) ), longList, }, }; };