Skip to content
This repository has been archived by the owner on Oct 20, 2024. It is now read-only.

Commit

Permalink
Merge branch 'release/v1.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagovtristao committed Aug 18, 2018
2 parents 30b039a + 78a1957 commit 7d30468
Show file tree
Hide file tree
Showing 19 changed files with 7,963 additions and 942 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dist/
lib/
es/
src/**/*.js
npm-debug.log

# Generated by rollup
.rpt2_cache/
.rpt2_cache/
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
language: node_js
node_js:
- "8"
before_install:
- npm i -g [email protected]
install:
- npm ci
script:
- npm run ci
# keep the npm cache around to speed up installs
cache:
directories:
- "$HOME/.npm"
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"editor.tabSize": 2,
"eslint.enable": false
}
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Change Log
==========

This project adheres to [Semantic Versioning](https://semver.org/).

Every release is documented on the Github [Releases](https://github.com/tiagovtristao/react-table-container/releases) page.
174 changes: 129 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,138 @@
# ReactTableContainer
ReactTableContainer
===================

A React component container for the HTML **\<table\>** element that allows it to be of any specified dimensions while locking its header during scrolling.
It wraps the HTML **\<table\>** element (or a component rendering it) in a container of any specified dimensions while keeping its header fixed to the top during scrolling.

### Motivation
* Table header event listeners are preserved;
* Tested on Chrome (Desktop & Mobile), Firefox, Safari, Edge and IE11.

Sometimes, you need your **\<table\>** element to have a specific height and width to fit in an area of your page, without having the tabular data within the element affecting these required dimensions, nor these dimensions affecting the layout of this element. Also, the **\<thead\>** element should ideally remain fixed/locked as you scroll down the data, so the context is not lost.
Installation
------------

This is what this React component aims to solve.

### Demos
```bash
npm install --save react-table-container
```

* https://codepen.io/tiagovtristao/full/eeyyOP/
Usage
-----

### Installation
**Example 1** (using HTML table elements only)

```
npm install --save react-table-container
```js
import React from "react";
import ReactTableContainer from "react-table-container";

const CustomHTMLTableResizedWithFixedHeader = () => (
<ReactTableContainer width="auto" height="500px">
<table>
<colgroup>
<col span="1" className="" />
...
</colgroup>
<thead>
<tr>
<th>Header cell</th>
...
</tr>
</thead>
<tbody>
<tr>
<td>Body cell</td>
...
</tr>
<tr>
<td>Body cell</td>
...
</tr>
...
</tbody>
</table>
</ReactTableContainer>
);

export default CustomHTMLTableResizedWithFixedHeader;
```

### Usage
**Example 2** (using `@material-ui`)

```js
import ReactTableContainer from 'react-table-container';

class MyTable extends Component {
render() {
return (
<ReactTableContainer width="auto" height="500px">
<table>
<thead>
<tr>
<th>Header cell</th>
...
</tr>
</thead>
<tbody>
<tr>
<td>Body cell</td>
...
</tr>
<tr>
<td>Body cell</td>
...
</tr>
...
</tbody>
</table>
import React from "react";
import { withStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import ReactTableContainer from "react-table-container";

let styles = theme => ({
root: {
display: "inline-block",
marginTop: theme.spacing.unit * 3
},
table: {
backgroundColor: "#ffffff",
minWidth: "700px"
},
});

let id = 0;
function createData(name, calories, fat, carbs, protein) {
id += 1;
return { id, name, calories, fat, carbs, protein };
}

let data = [
createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
createData("Eclair", 262, 16.0, 24, 6.0),
createData("Cupcake", 305, 3.7, 67, 4.3),
createData("Gingerbread", 356, 16.0, 49, 3.9)
];

function CustomMaterialUITableResizedWithFixedHeader(props) {
const { classes } = props;

return (
<Paper className={classes.root}>
<ReactTableContainer width="auto" height="200px">
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell numeric>Calories</TableCell>
<TableCell numeric>Fat (g)</TableCell>
<TableCell numeric>Carbs (g)</TableCell>
<TableCell numeric>Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ data.map(n =>
<TableRow key={n.id}>
<TableCell>{n.name}</TableCell>
<TableCell numeric>{n.calories}</TableCell>
<TableCell numeric>{n.fat}</TableCell>
<TableCell numeric>{n.carbs}</TableCell>
<TableCell numeric>{n.protein}</TableCell>
</TableRow>
) }
</TableBody>
</Table>
</ReactTableContainer>
);
}
</Paper>
);
}

export default withStyles(styles)(MaterialUITableResizedWithFixedHeader);
```

**Note**: The table's header mustn't be transparent, otherwise the body content will appear under it on scroll.
> **REQUIRED**
>
> The table's header mustn't be transparent, otherwise the body content will appear under it on scroll.
### Options
Options
-------

* `width`: Any valid CSS value. **Required**.
* `height`: Any valid CSS value. **Required**.
Expand Down Expand Up @@ -82,12 +160,18 @@ class MyTable extends Component {
}
```

### Contributing
Limitations
-----------

* HTML `caption` and `tfoot` table elements are currently not supported. Using them might cause unexpected behaviour.

Contributing
------------

* The code base is written using TypeScript;
* Feel free to send pull requests for bug fixing. But make sure you run `npm run lint` and `npm run prettify` before;
* Feel free to send pull requests for bug fixing. But make sure that running `npm run ci` succeeds before doing so;
* Please open an issue first for new features/ideas.

### License
License
-------

MIT
115 changes: 115 additions & 0 deletions __tests__/container-dimensions.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import * as React from "react";
import * as ReactDOMServer from 'react-dom/server';
import ReactTableContainer from "../src/index";

const containerHTML = (dimensions) => (
ReactDOMServer.renderToString(
<ReactTableContainer width={dimensions.width} height={dimensions.height}>
<table>
<thead>
<tr>
<th>Header cell</th>
<th>Header cell</th>
<th>Header cell</th>
</tr>
</thead>
<tbody>
<tr>
<td>Body cell</td>
<td>Body cell</td>
<td>Body cell</td>
</tr>
<tr>
<td>Body cell</td>
<td>Body cell</td>
<td>Body cell</td>
</tr>
</tbody>
</table>
</ReactTableContainer>
)
);

describe('container', () => {
let page;

beforeAll(async () => {
page = await (global as any).__BROWSER__.newPage();

await page.setContent(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
#app {
width: 1000px;
height: 1000px;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
</html>
`);
});

afterAll(async () => {
await page.close();
});

const attachTable = async (dimensions) => {
await page.evaluate(html => {
document.querySelector('#app').innerHTML = html;
}, containerHTML(dimensions));
};

const getComputedDimensions = async (selector) => {
let computedDimensions = await page.evaluate(s => {
let el = document.querySelector(s);

return {
width: window.getComputedStyle(el).width,
height: window.getComputedStyle(el).height,
}
}, selector);

return computedDimensions;
};

it('renders \'100px\' by \'100px\'', async () => {
expect.assertions(2);

await attachTable({ width: '100px', height: '100px' });

let { width, height } = await getComputedDimensions('#app > div');

expect(width).toBe('100px');
expect(height).toBe('100px');
});

it('renders \'100%\' by \'100%\' (ie. the dimensions of the parent component)', async () => {
expect.assertions(2);

await attachTable({ width: '100%', height: '100%' });

let parentContainerDimensions = await getComputedDimensions('#app');
let containerDimensions = await getComputedDimensions('#app > div');

expect(containerDimensions.width).toBe(parentContainerDimensions.width);
expect(containerDimensions.height).toBe(parentContainerDimensions.height);
});

it('renders \'auto\' by \'auto\' (ie. the dimensions of the table itself)', async () => {
expect.assertions(2);

await attachTable({ width: 'auto', height: 'auto' });

let containerDimensions = await getComputedDimensions('#app > div');
let tableDimensions = await getComputedDimensions('#app table[data-rtc-id="main-table"]');

expect(containerDimensions.width).toBe(tableDimensions.width);
expect(containerDimensions.height).toBe(tableDimensions.height);
});
});
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ interface Props {
maxHeight?: string;
}

export default class ReactTableContainer extends React.Component<Props> {}
export default class ReactTableContainer extends React.Component<Props> {}

18 changes: 18 additions & 0 deletions jest-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const chalk = require("chalk");
const puppeteer = require("puppeteer");
const fs = require("fs");
const mkdirp = require("mkdirp");
const os = require("os");
const path = require("path");

const DIR = path.join(os.tmpdir(), "jest_puppeteer_global_setup");

module.exports = async function() {
console.log(chalk.green("Setup Puppeteer"));
const browser = await puppeteer.launch({ args: ["--no-sandbox"] });
// This global is not available inside tests but only in global teardown
global.__BROWSER_GLOBAL__ = browser;
// Instead, we expose the connection details via file system to be used in tests
mkdirp.sync(DIR);
fs.writeFileSync(path.join(DIR, "wsEndpoint"), browser.wsEndpoint());
};
12 changes: 12 additions & 0 deletions jest-teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const chalk = require("chalk");
const rimraf = require("rimraf");
const os = require("os");
const path = require("path");

const DIR = path.join(os.tmpdir(), "jest_puppeteer_global_setup");

module.exports = async function() {
console.log(chalk.green("Teardown Puppeteer"));
await global.__BROWSER_GLOBAL__.close();
rimraf.sync(DIR);
};
Loading

0 comments on commit 7d30468

Please sign in to comment.