From f83b39838a586db07a2f076f2cd247ae2272b023 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Tue, 3 Jul 2018 12:30:34 -0700 Subject: [PATCH] Styleguide entry for tables --- ui/app/components/freestyle/sg-table.js | 159 +++++++++++ ui/app/controllers/freestyle.js | 10 + .../components/freestyle/sg-table.hbs | 257 ++++++++++++++++++ ui/app/templates/freestyle.hbs | 6 + 4 files changed, 432 insertions(+) create mode 100644 ui/app/components/freestyle/sg-table.js create mode 100644 ui/app/templates/components/freestyle/sg-table.hbs diff --git a/ui/app/components/freestyle/sg-table.js b/ui/app/components/freestyle/sg-table.js new file mode 100644 index 000000000..29c1020b1 --- /dev/null +++ b/ui/app/components/freestyle/sg-table.js @@ -0,0 +1,159 @@ +import Component from '@ember/component'; +import { computed } from '@ember/object'; + +export default Component.extend({ + searchTerm: '', + + currentPage: 1, + sortProperty: 'name', + sortDescending: false, + + shortList: [ + { + name: 'Nomad', + lang: 'golang', + desc: + 'Nomad is a flexible, enterprise-grade cluster scheduler designed to easily integrate into existing workflows. Nomad can run a diverse workload of micro-service, batch, containerized and non-containerized applications.', + link: 'https://www.nomadproject.io/', + }, + { + name: 'Terraform', + lang: 'golang', + desc: + 'Terraform is a tool for building, changing, and combining infrastructure safely and efficiently.', + link: 'https://www.terraform.io/', + }, + { + name: 'Vault', + lang: 'golang', + desc: + 'A tool for secrets management, encryption as a service, and privileged access management', + link: 'https://www.vaultproject.io/', + }, + { + name: 'Consul', + lang: 'golang', + desc: + 'Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.', + link: 'https://www.consul.io/', + }, + { + name: 'Vagrant', + lang: 'ruby', + desc: 'Vagrant is a tool for building and distributing development environments.', + link: 'https://www.vagrantup.com/', + }, + { + name: 'Packer', + lang: 'golang', + desc: + 'Packer is a tool for creating identical machine images for multiple platforms from a single source configuration.', + link: 'https://www.packer.io/', + }, + ], + + 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' }, + ], + + filteredShortList: computed('searchTerm', 'shortList.[]', function() { + const term = this.get('searchTerm').toLowerCase(); + return this.get('shortList').filter(product => product.name.toLowerCase().includes(term)); + }), + + sortedShortList: computed('shortList.[]', 'sortProperty', 'sortDescending', function() { + const sorted = this.get('shortList').sortBy(this.get('sortProperty')); + return this.get('sortDescending') ? sorted.reverse() : sorted; + }), +}); diff --git a/ui/app/controllers/freestyle.js b/ui/app/controllers/freestyle.js index a5809f613..f585f0460 100644 --- a/ui/app/controllers/freestyle.js +++ b/ui/app/controllers/freestyle.js @@ -2,5 +2,15 @@ import { inject as service } from '@ember/service'; import FreestyleController from 'ember-freestyle/controllers/freestyle'; export default FreestyleController.extend({ + queryParams: { + currentPage: 'page', + sortProperty: 'sort', + sortDescending: 'desc', + }, + + currentPage: 1, + sortProperty: 'name', + sortDescending: true, + emberFreestyle: service(), }); diff --git a/ui/app/templates/components/freestyle/sg-table.hbs b/ui/app/templates/components/freestyle/sg-table.hbs new file mode 100644 index 000000000..fffa79ef0 --- /dev/null +++ b/ui/app/templates/components/freestyle/sg-table.hbs @@ -0,0 +1,257 @@ +{{#freestyle-usage "table-simple" title="Table"}} + {{#list-table source=shortList as |t|}} + {{#t.head}} + Name + Language + Description + {{/t.head}} + {{#t.body key="model.name" as |row|}} + + {{row.model.name}} + {{row.model.lang}} + {{row.model.desc}} + + {{/t.body}} + {{/list-table}} +{{/freestyle-usage}} +{{#freestyle-annotation}} +

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

+{{/freestyle-annotation}} + +{{#freestyle-usage "table-search" title="Table Search"}} +
+
+ Table Name + {{search-box + searchTerm=(mut searchTerm) + placeholder="Search..." + class="is-inline pull-right" + inputClass="is-compact"}} +
+
+ {{#if filteredShortList.length}} + {{#list-table source=filteredShortList as |t|}} + {{#t.head}} + Name + Language + Description + {{/t.head}} + {{#t.body key="model.name" as |row|}} + + {{row.model.name}} + {{row.model.lang}} + {{row.model.desc}} + + {{/t.body}} + {{/list-table}} + {{else}} +
+

No Matches

+

No products match your query.

+
+ {{/if}} +
+
+{{/freestyle-usage}} +{{#freestyle-annotation}} +

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

+{{/freestyle-annotation}} + +{{#freestyle-usage "table-sortable-columns" title="Table Sortable Columns"}} + {{#list-table + source=sortedShortList + sortProperty=sortProperty + sortDescending=sortDescending as |t|}} + {{#t.head}} + {{#t.sort-by prop="name"}}Name{{/t.sort-by}} + {{#t.sort-by prop="lang" class="is-2"}}Language{{/t.sort-by}} + Description + {{/t.head}} + {{#t.body key="model.name" as |row|}} + + {{row.model.name}} + {{row.model.lang}} + {{row.model.desc}} + + {{/t.body}} + {{/list-table}} +{{/freestyle-usage}} +{{#freestyle-annotation}} +

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).

+{{/freestyle-annotation}} + +{{#freestyle-usage "table-multi-row" title="Table Multi-row"}} + {{#list-table + source=sortedShortList + sortProperty=sortProperty + sortDescending=sortDescending + class="is-striped" as |t|}} + {{#t.head}} + {{#t.sort-by prop="name"}}Name{{/t.sort-by}} + {{#t.sort-by prop="lang"}}Language{{/t.sort-by}} + {{/t.head}} + {{#t.body key="model.name" as |row|}} + + {{row.model.name}} + {{row.model.lang}} + + + {{row.model.desc}} + + {{/t.body}} + {{/list-table}} +{{/freestyle-usage}} +{{#freestyle-annotation}} +

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.

+{{/freestyle-annotation}} + +{{#freestyle-usage "table-pagination" title="Table Pagination"}} + {{#list-pagination source=longList size=5 page=currentPage as |p|}} + {{#list-table source=p.list class="with-foot" as |t|}} + {{#t.head}} + Rank + City + State + Population + Growth + {{/t.head}} + {{#t.body key="model.rank" as |row|}} + + {{row.model.rank}} + {{row.model.city}} + {{row.model.state}} + {{row.model.population}} + {{format-percentage row.model.growth total=1}} + + {{/t.body}} + {{/list-table}} +
+ +
+ {{/list-pagination}} +{{/freestyle-usage}} +{{#freestyle-annotation}} +

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.

+{{/freestyle-annotation}} + +{{#freestyle-usage "table-row-links" title="Table Row Links"}} + {{#list-table source=shortList as |t|}} + {{#t.head}} + Name + Language + Description + {{/t.head}} + {{#t.body key="model.name" as |row|}} + + {{row.model.name}} + {{row.model.lang}} + {{row.model.desc}} + + {{/t.body}} + {{/list-table}} +{{/freestyle-usage}} +{{#freestyle-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 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. +
  3. 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.
  4. +
  5. 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.
  6. +
+{{/freestyle-annotation}} + +{{#freestyle-usage "table-cell-links" title="Table Cell Links"}} + {{#list-table source=shortList as |t|}} + {{#t.head}} + Name + Language + Description + {{/t.head}} + {{#t.body key="model.name" as |row|}} + + {{row.model.name}} + {{row.model.lang}} + {{row.model.desc}} + + {{/t.body}} + {{/list-table}} +{{/freestyle-usage}} +{{#freestyle-annotation}} +

Links in table cells are just links.

+{{/freestyle-annotation}} + +{{#freestyle-usage "table-cell-decorations" title="Table Cell Decorations"}} + {{#list-table source=shortList as |t|}} + {{#t.head}} + Name + Language + Description + {{/t.head}} + {{#t.body key="model.name" as |row|}} + + {{row.model.name}} + + + {{row.model.lang}} + + {{row.model.desc}} + + {{/t.body}} + {{/list-table}} +{{/freestyle-usage}} +{{#freestyle-annotation}} +

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

+{{/freestyle-annotation}} + +{{#freestyle-usage "table-cell-icons" title="Table Cell Icons"}} + {{#list-pagination source=longList size=5 page=currentPage as |p|}} + {{#list-table source=p.list class="with-foot" as |t|}} + {{#t.head}} + + Rank + City + State + Population + Growth + {{/t.head}} + {{#t.body key="model.rank" as |row|}} + + + {{#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}} + + {{/t.body}} + {{/list-table}} +
+ +
+ {{/list-pagination}} +{{/freestyle-usage}} diff --git a/ui/app/templates/freestyle.hbs b/ui/app/templates/freestyle.hbs index 3ca493348..19fbd0679 100644 --- a/ui/app/templates/freestyle.hbs +++ b/ui/app/templates/freestyle.hbs @@ -71,8 +71,14 @@ {{#section.subsection name="Search box"}} {{freestyle/sg-search-box}} {{/section.subsection}} + {{#section.subsection name="Table"}} + {{freestyle/sg-table + sortProperty=sortProperty + sortDescending=sortDescending + currentPage=currentPage}} {{/section.subsection}} + {{#section.subsection name="Table, Configuration"}} {{/section.subsection}}