Permalink
Browse files

feat(table): BS V4.beta.2 New responsive breakpoints and table-dark c…

…lass (#1222)

* fix(table): New responive breakpoints and table-dark class

V4.beta.2 adds responsive breakpoint support, and changes class `table-inverse` to `table-dark`

* docs(table): Updated docs

* [table] Updte tests

* Update README.md

* [table] Deprecated 'inverse' prop for new 'dark' prop

To align more closely with new class name

* update test fixture for 'dark' prop

* Update test spec for 'dark' prop

* Update table.vue

* Update table.vue

* Update readme with new `dark` prop

* ESlint fixes

* Update README.md

* Update table.spec.js

* Update demo.html

* Update table.vue

* Update table.vue
  • Loading branch information...
tmorehouse committed Oct 23, 2017
1 parent c37cef4 commit febdfd1517e5182e9a69f33d5f7da27fa9427423
Showing with 115 additions and 48 deletions.
  1. +64 −9 docs/components/table/README.md
  2. +32 −20 lib/components/table.vue
  3. +14 −14 tests/components/table.spec.js
  4. +5 −5 tests/fixtures/table/demo.html
@@ -290,12 +290,15 @@ fields: [
| `bordered` | For borders on all sides of the table and cells.
| `small` | To make tables more compact by cutting cell padding in half.
| `hover` | To enable a hover highlighting state on table rows within a `<tbody>`
| `inverse` | Invert the colors — with light text on dark backgrounds
| `responsive` | Generate a responsive table to make it scroll horizontally on small devices (under 768px)
| `dark` | Invert the colors — with light text on dark backgrounds (equivalent to Bootstrap V4 class `.table-dark`)
| `responsive` | Generate a responsive table to make it scroll horizontally. Set to `true` for an always responsive table, or set it to one of the breakpoints `sm`, `md`, `lg`, or `xl` to make the table responsive (horizontally scroll) only on screens smaller than the breakpoint.
| `fixed` | Gnerate a table with equal fixed-width columns (`table-layout: fixed`)
| `foot-clone` | Turns on the table footer, and defaults with the same contents a the table header
| `head-variant` | Use `default` or `inverse` to make table header appear light or dark gray, respectively
| `foot-variant` | Use `default` or `inverse` to make table footer appear light or dark gray, respectively. IF not set, used `head-variant`. Has no effect if `foot-clone` is not set
| `head-variant` | Use `light` or `dark` to make table header appear light or dark gray, respectively
| `foot-variant` | Use `light` or `dark` to make table footer appear light or dark gray, respectively. If not set, `head-variant` will be used. Has no effect if `foot-clone` is not set

**Deprecation note:** As of Bootstrap-Vue v1.0.0-beta.10, the prop `inverse` has been deprecated in
favour of prop `dark` to better align with Bootstrap V4.beta.2 CSS class names.

**Example: Bordered table**
```html
@@ -341,10 +344,10 @@ export default {
<!-- table-small.vue -->
```

**Example: Inverse table**
**Example: Dark table**
```html
<template>
<b-table inverse :items="items" :fields="fields"></b-table>
<b-table dark :items="items" :fields="fields"></b-table>
</template>

<script>
@@ -385,7 +388,59 @@ export default {
<!-- table-footer.vue -->
```

## Table `<caption>`
## Responsive tables
Responsive tables allow tables to be scrolled horizontally with ease. Make any table
responsive across all viewports by setting the prop `responsive` to `true`. Or, pick a
maximum breakpoint with which to have a responsive table up to by setting the prop
`responsive` to one of the breakpoint values: `sm`, `md`, `lg`, or `xl`.

**Note: Possible vertical clipping/truncation**

Responsive tables make use of `overflow-y: hidden`, which clips off any content that
goes beyond the bottom or top edges of the table. In particular, this can clip off
dropdown menus and other third-party widgets.

**Example: Always responsive table**
```html
<template>
<b-table responsive :items="items"></b-table>
</template>

<script>
export default {
data: {
items: [
{
'heading 1': 'table cell', 'heading 2': 'table cell',
'heading 3': 'table cell', 'heading 4': 'table cell',
'heading 5': 'table cell', 'heading 6': 'table cell',
'heading 7': 'table cell', 'heading 8': 'table cell',
'heading 9': 'table cell', 'heading 10': 'table cell'
},
{
'heading 1': 'table cell', 'heading 2': 'table cell',
'heading 3': 'table cell', 'heading 4': 'table cell',
'heading 5': 'table cell', 'heading 6': 'table cell',
'heading 7': 'table cell', 'heading 8': 'table cell',
'heading 9': 'table cell', 'heading 10': 'table cell'
},
{
'heading 1': 'table cell', 'heading 2': 'table cell',
'heading 3': 'table cell', 'heading 4': 'table cell',
'heading 5': 'table cell', 'heading 6': 'table cell',
'heading 7': 'table cell', 'heading 8': 'table cell',
'heading 9': 'table cell', 'heading 10': 'table cell'
}
]
}
};
</script>

<!-- table-responsive.vue -->
```


## Table caption
Add an optional caption to your table via the prop `caption` or the named
slot `table-caption` (the slot takes precedence over the prop). The default
Bootstrap V4 styling places the caption at the bottom of the table. You can
@@ -416,11 +471,11 @@ export default {
<!-- table-caption.vue -->
```

## Table `<colgroup>`
## Table colgroup
Use the named slot `table-colgroup` to specify `<colgroup>` and `<col>` elements
for optional grouping and styling of table columns. Note the styles available via `<col>`
elements are limited. Refer to [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
for details and usage.
for details and usage of `<colgroup>`


## Custom Data Rendering
@@ -125,9 +125,9 @@
</template>

<script>
import { warn, pluckProps, looseEqual } from '../utils';
import { warn, looseEqual } from '../utils';
import { keys, assign } from '../utils/object';
import { isArray } from '../utils/array'
import { isArray } from '../utils/array';
import { listenOnRootMixin } from '../mixins';
import startCase from 'lodash.startcase';
@@ -148,7 +148,7 @@ function recToString(obj) {
return toString(keys(obj).reduce((o, k) => {
// Ignore fields that start with _
if (!/^_/.test(k)) {
if (!(/^_/).test(k)) {
o[k] = obj[k];
}
return o;
@@ -168,16 +168,16 @@ function processField(key, value) {
let field = null;
if (typeof value === 'string') {
// Label shortcut
field = { key: key, label: value };
field = { key, label: value };
} else if (typeof value === 'function') {
// Formatter shortcut
field = { key: key, formatter: value };
field = { key, formatter: value };
} else if (typeof value === 'object') {
field = assign({}, value);
field.key = field.key || key;
} else if (value !== false) {
// Fallback to just key
field = { key: key };
field = { key };
}
return field;
}
@@ -233,9 +233,21 @@ export default {
type: Boolean,
default: false
},
dark: {
type: Boolean,
default() {
if (this && typeof this.inverse === 'boolean') {
// Deprecate inverse
warn("b-table: prop 'inverse' has been deprecated. Use 'dark' instead");
return this.bark;
}
return false;
}
},
inverse: {
// Deprecated in v1.0.0.beta.10 in favor of `dark`
type: Boolean,
default: false
default: null
},
hover: {
type: Boolean,
@@ -246,7 +258,7 @@ export default {
default: false
},
responsive: {
type: Boolean,
type: [Boolean, String],
default: false
},
fixed: {
@@ -333,7 +345,7 @@ export default {
}
},
context(newVal, oldVal) {
if(!looseEqual(newVal, oldVal)) {
if (!looseEqual(newVal, oldVal)) {
this.$emit('context-changed', newVal);
}
},
@@ -408,14 +420,15 @@ export default {
},
computed: {
tableClasses() {
const responsive = this.responsive === '' ? true : this.responsive;
return [
'table',
'b-table',
this.striped ? 'table-striped' : '',
this.hover ? 'table-hover' : '',
this.inverse ? 'table-inverse' : '',
this.dark ? 'table-dark' : '',
this.bordered ? 'table-bordered' : '',
this.responsive ? 'table-responsive' : '',
responsive === true ? 'table-responsive' : (Boolean(responsive) ? `table-responsive-${responsive}` : ''),
this.fixed ? 'table-fixed' : '',
this.small ? 'table-sm' : ''
];
@@ -470,11 +483,11 @@ export default {
fields.push(field);
}
}
})
});
} else if (this.fields && typeof this.fields === 'object' && keys(this.fields).length > 0) {
// Normalize object Form
keys(this.fields).forEach(key => {
let field = processField(key, this.fields[key])
let field = processField(key, this.fields[key]);
if (field) {
fields.push(field);
}
@@ -483,7 +496,7 @@ export default {
// If no field provided, take a sample from first record (if exits)
if (fields.length === 0 && this.computedItems.length > 0) {
const sample = this.computedItems[0]
const sample = this.computedItems[0];
keys(sample).forEach(k => {
fields.push({ key: k , label: startCase(k)});
});
@@ -496,9 +509,8 @@ export default {
memo[f.key] = true;
f.label = f.label || startCase(f.key);
return true;
} else {
return false;
}
return false;
});
},
computedItems() {
@@ -591,18 +603,18 @@ export default {
tdClasses(field, item) {
let cellVariant = '';
if (item._cellVariants && item._cellVariants[field.key]) {
cellVariant = (this.inverse ? 'bg-' : 'table-') + item._cellVariants[field.key];
cellVariant = `${this.dark ? 'bg' : 'table'}-${item._cellVariants[field.key]}`;
}
return [
(field.variant && !cellVariant) ? ((this.inverse ? 'bg-' : 'table-') + field.variant) : '',
(field.variant && !cellVariant) ? `${this.dark ? 'bg' : 'table'}-${field.variant}` : '',
cellVariant,
field.class ? field.class : '',
field.tdClass ? field.tdClass : ''
];
},
rowClasses(item) {
return [
item._rowVariant ? ((this.inverse ? 'bg-' : 'table-') + item._rowVariant) : ''
item._rowVariant ? `${this.dark ? 'bg' : 'table'}-${item._rowVariant}` : ''
];
},
rowClicked(e, item, index) {
@@ -713,7 +725,7 @@ export default {
return value;
}
}
}
};
</script>

<style>
@@ -19,12 +19,12 @@ describe("table", async () => {
"table-responsive"
]);

expect($refs.table_inverse).toHaveAllClasses([
expect($refs.table_dark).toHaveAllClasses([
"table",
"b-table",
"table-sm",
"table-bordered",
"table-inverse"
"table-dark"
]);
});

@@ -58,10 +58,10 @@ describe("table", async () => {
expect(tfoot).toBeDefined();
});

it("table_inverse should have thead and tbody", async () => {
it("table_dark should have thead and tbody", async () => {
const { app: { $refs, $el } } = window;

const parts = [...$refs.table_inverse.$el.children];
const parts = [...$refs.table_dark.$el.children];

const thead = parts.find(el => el.tagName && el.tagName === "THEAD");
expect(thead).toBeDefined();
@@ -73,28 +73,28 @@ describe("table", async () => {
expect(tfoot).not.toBeDefined();
});

it("table_paginated thead should contain class thead-inverse", async () => {
it("table_paginated thead should contain class thead-dark", async () => {
const { app: { $refs, $el } } = window;
const thead = [...$refs.table_paginated.$el.children].find(el => el && el.tagName === "THEAD");
expect(thead).toBeDefined();
if (thead) {
expect(thead.classList.contains("thead-inverse")).toBe(true);
expect(thead.classList.contains("thead-dark")).toBe(true);
}
});

it("table_paginated tfoot should contain class thead-default", async () => {
it("table_paginated tfoot should contain class thead-light", async () => {
const { app: { $refs, $el } } = window;
const tfoot = [...$refs.table_paginated.$el.children].find(el => el && el.tagName === "TFOOT");
expect(tfoot).toBeDefined();
if (tfoot) {
expect(tfoot.classList.contains("thead-default")).toBe(true);
expect(tfoot.classList.contains("thead-light")).toBe(true);
}
});

it("all examples have correct number of columns", async () => {
const { app: { $refs, $el } } = window;

const tables = ["table_basic", "table_paginated", "table_inverse"];
const tables = ["table_basic", "table_paginated", "table_dark"];

tables.forEach((table, idx) => {
const vm = $refs[table];
@@ -114,7 +114,7 @@ describe("table", async () => {
const { app: { $refs, $el } } = window;
const app = window.app;

const tables = ["table_basic", "table_paginated", "table_inverse"];
const tables = ["table_basic", "table_paginated", "table_dark"];

tables.forEach((table, idx) => {
const vm = $refs[table];
@@ -129,7 +129,7 @@ describe("table", async () => {
it("all examples have sortable & unsortable headers", async () => {
const { app: { $refs, $el } } = window;

const tables = ["table_basic", "table_paginated", "table_inverse"];
const tables = ["table_basic", "table_paginated", "table_dark"];
const sortables = [true, true, false, false];

tables.forEach(table => {
@@ -174,7 +174,7 @@ describe("table", async () => {
it('all example tables should have attribute aria-busy="false" when busy is false', async () => {
const { app: { $refs, $el } } = window;

const tables = ["table_basic", "table_paginated", "table_inverse"];
const tables = ["table_basic", "table_paginated", "table_dark"];

await setData(app, "isBusy", false);
await nextTick();
@@ -239,7 +239,7 @@ describe("table", async () => {
const { app: { $refs, $el } } = window;
const app = window.app;

const tables = ["table_basic", "table_paginated", "table_inverse"];
const tables = ["table_basic", "table_paginated", "table_dark"];

const items = app.items.slice();
items[0]._rowVariant = "success";
@@ -252,7 +252,7 @@ describe("table", async () => {
expect(tbody).toBeDefined();
if (tbody) {
const tr = tbody.children[0];
const variant = vm.inverse ? "bg-success" : "table-success";
const variant = vm.dark ? "bg-success" : "table-success";
expect(Boolean(tr) && Boolean(tr.classList) && tr.classList.contains(variant)).toBe(true);
}
});
Oops, something went wrong.

0 comments on commit febdfd1

Please sign in to comment.