Repository: mui/material-ui (TypeScript) — Feature

Issue:
[Table] Add padding attribute to the component
The ``padding`` attribute in the ``TableCell`` is useful but it's kind of a pain in the ass to add it to every cell (or cell generation loops) if we want the whole ``Table`` to be ``dense`` for example.

I think that it would be great to have ``<Table padding="dense">...</Table>`` that would apply the padding to the whole table. If it's defined in the child component, it should override the parent attribute value.


Discussion:
@HRK44 I agree, we can solve this pain point.

Resolving patch:
diff --git a/packages/material-ui/src/Table/Table.js b/packages/material-ui/src/Table/Table.js
--- a/packages/material-ui/src/Table/Table.js
+++ b/packages/material-ui/src/Table/Table.js
@@ -18,7 +18,9 @@ class Table extends React.Component {
   getChildContext() {
     // eslint-disable-line class-methods-use-this
     return {
-      table: {},
+      table: {
+        padding: this.props.padding,
+      },
     };
   }
 
@@ -48,10 +50,15 @@ Table.propTypes = {
    * Either a string to use a DOM element or a component.
    */
   component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+  /**
+   * Allows TableCells to inherit padding of the Table.
+   */
+  padding: PropTypes.oneOf(['default', 'checkbox', 'dense', 'none']),
 };
 
 Table.defaultProps = {
   component: 'table',
+  padding: 'default',
 };
 
 Table.childContextTypes = {
diff --git a/packages/material-ui/src/TableBody/TableBody.js b/packages/material-ui/src/TableBody/TableBody.js
--- a/packages/material-ui/src/TableBody/TableBody.js
+++ b/packages/material-ui/src/TableBody/TableBody.js
@@ -14,8 +14,8 @@ class TableBody extends React.Component {
   getChildContext() {
     // eslint-disable-line class-methods-use-this
     return {
-      table: {
-        body: true,
+      tablelvl2: {
+        variant: 'body',
       },
     };
   }
@@ -53,7 +53,7 @@ TableBody.defaultProps = {
 };
 
 TableBody.childContextTypes = {
-  table: PropTypes.object,
+  tablelvl2: PropTypes.object,
 };
 
 export default withStyles(styles, { name: 'MuiTableBody' })(TableBody);
diff --git a/packages/material-ui/src/TableCell/TableCell.js b/packages/material-ui/src/TableCell/TableCell.js
--- a/packages/material-ui/src/TableCell/TableCell.js
+++ b/packages/material-ui/src/TableCell/TableCell.js
@@ -75,30 +75,34 @@ function TableCell(props, context) {
     component,
     sortDirection,
     numeric,
-    padding,
+    padding: paddingProp,
     scope: scopeProp,
     variant,
     ...other
   } = props;
-  const { table } = context;
+
+  const { table, tablelvl2 } = context;
   let Component;
   if (component) {
     Component = component;
   } else {
-    Component = table && table.head ? 'th' : 'td';
+    Component = tablelvl2 && tablelvl2.variant === 'head' ? 'th' : 'td';
   }
 
   let scope = scopeProp;
-  if (!scope && table && table.head) {
+  if (!scope && tablelvl2 && tablelvl2.variant === 'head') {
     scope = 'col';
   }
+  const padding = paddingProp || (table && table.padding ? table.padding : 'default');
 
   const className = classNames(
     classes.root,
     {
-      [classes.head]: variant ? variant === 'head' : table && table.head,
-      [classes.body]: variant ? variant === 'body' : table && table.body,
-      [classes.footer]: variant ? variant === 'footer' : table && table.footer,
+      [classes.head]: variant ? variant === 'head' : tablelvl2 && tablelvl2.variant === 'head',
+      [classes.body]: variant ? variant === 'body' : tablelvl2 && tablelvl2.variant === 'body',
+      [classes.footer]: variant
+        ? variant === 'footer'
+        : tablelvl2 && tablelvl2.variant === 'footer',
       [classes.numeric]: numeric,
       [classes[`padding${capitalize(padding)}`]]: padding !== 'default',
     },
@@ -142,6 +146,7 @@ TableCell.propTypes = {
   numeric: PropTypes.bool,
   /**
    * Sets the padding applied to the cell.
+   * By default, the Table parent component set the value.
    */
   padding: PropTypes.oneOf(['default', 'checkbox', 'dense', 'none']),
   /**
@@ -161,11 +166,11 @@ TableCell.propTypes = {
 
 TableCell.defaultProps = {
   numeric: false,
-  padding: 'default',
 };
 
 TableCell.contextTypes = {
-  table: PropTypes.object.isRequired,
+  table: PropTypes.object,
+  tablelvl2: PropTypes.object,
 };
 
 export default withStyles(styles, { name: 'MuiTableCell' })(TableCell);
diff --git a/packages/material-ui/src/TableFooter/TableFooter.js b/packages/material-ui/src/TableFooter/TableFooter.js
--- a/packages/material-ui/src/TableFooter/TableFooter.js
+++ b/packages/material-ui/src/TableFooter/TableFooter.js
@@ -14,8 +14,8 @@ class TableFooter extends React.Component {
   getChildContext() {
     // eslint-disable-line class-methods-use-this
     return {
-      table: {
-        footer: true,
+      tablelvl2: {
+        variant: 'footer',
       },
     };
   }
@@ -53,7 +53,7 @@ TableFooter.defaultProps = {
 };
 
 TableFooter.childContextTypes = {
-  table: PropTypes.object,
+  tablelvl2: PropTypes.object,
 };
 
 export default withStyles(styles, { name: 'MuiTableFooter' })(TableFooter);
diff --git a/packages/material-ui/src/TableHead/TableHead.js b/packages/material-ui/src/TableHead/TableHead.js
--- a/packages/material-ui/src/TableHead/TableHead.js
+++ b/packages/material-ui/src/TableHead/TableHead.js
@@ -14,8 +14,8 @@ class TableHead extends React.Component {
   getChildContext() {
     // eslint-disable-line class-methods-use-this
     return {
-      table: {
-        head: true,
+      tablelvl2: {
+        variant: 'head',
       },
     };
   }
@@ -53,7 +53,7 @@ TableHead.defaultProps = {
 };
 
 TableHead.childContextTypes = {
-  table: PropTypes.object,
+  tablelvl2: PropTypes.object,
 };
 
 export default withStyles(styles, { name: 'MuiTableHead' })(TableHead);
diff --git a/packages/material-ui/src/TableRow/TableRow.js b/packages/material-ui/src/TableRow/TableRow.js
--- a/packages/material-ui/src/TableRow/TableRow.js
+++ b/packages/material-ui/src/TableRow/TableRow.js
@@ -25,15 +25,15 @@ export const styles = theme => ({
           : 'rgba(255, 255, 255, 0.14)',
     },
   },
-  /* Styles applied to the root element if `context.table` & `selected={true}`. */
+  /* Styles applied to the root element if `selected={true}`. */
   selected: {},
-  /* Styles applied to the root element if `context.table` & `hover={true}`. */
+  /* Styles applied to the root element if `hover={true}`. */
   hover: {},
-  /* Styles applied to the root element if `context.table.head`. */
+  /* Styles applied to the root element if table variant = 'head'. */
   head: {
     height: 56,
   },
-  /* Styles applied to the root element if `context.table.footer`. */
+  /* Styles applied to the root element if table variant = 'footer'. */
   footer: {
     height: 56,
   },
@@ -52,15 +52,15 @@ function TableRow(props, context) {
     selected,
     ...other
   } = props;
-  const { table } = context;
+  const { tablelvl2 } = context;
 
   const className = classNames(
     classes.root,
     {
-      [classes.head]: table && table.head,
-      [classes.footer]: table && table.footer,
-      [classes.hover]: table && hover,
-      [classes.selected]: table && selected,
+      [classes.head]: tablelvl2 && tablelvl2.variant === 'head',
+      [classes.footer]: tablelvl2 && tablelvl2.variant === 'footer',
+      [classes.hover]: hover,
+      [classes.selected]: selected,
     },
     classNameProp,
   );
@@ -104,7 +104,7 @@ TableRow.defaultProps = {
 };
 
 TableRow.contextTypes = {
-  table: PropTypes.object,
+  tablelvl2: PropTypes.object,
 };
 
 export default withStyles(styles, { name: 'MuiTableRow' })(TableRow);
diff --git a/pages/api/table-cell.md b/pages/api/table-cell.md
--- a/pages/api/table-cell.md
+++ b/pages/api/table-cell.md
@@ -19,7 +19,7 @@ title: TableCell API
 | <span class="prop-name">classes</span> | <span class="prop-type">object |   | Override or extend the styles applied to the component. See [CSS API](#css-api) below for more details. |
 | <span class="prop-name">component</span> | <span class="prop-type">union:&nbsp;string&nbsp;&#124;<br>&nbsp;func&nbsp;&#124;<br>&nbsp;object<br> |   | The component used for the root node. Either a string to use a DOM element or a component. |
 | <span class="prop-name">numeric</span> | <span class="prop-type">bool | <span class="prop-default">false</span> | If `true`, content will align to the right. |
-| <span class="prop-name">padding</span> | <span class="prop-type">enum:&nbsp;'default'&nbsp;&#124;<br>&nbsp;'checkbox'&nbsp;&#124;<br>&nbsp;'dense'&nbsp;&#124;<br>&nbsp;'none'<br> | <span class="prop-default">'default'</span> | Sets the padding applied to the cell. |
+| <span class="prop-name">padding</span> | <span class="prop-type">enum:&nbsp;'default'&nbsp;&#124;<br>&nbsp;'checkbox'&nbsp;&#124;<br>&nbsp;'dense'&nbsp;&#124;<br>&nbsp;'none'<br> |   | Sets the padding applied to the cell. By default, the Table parent component set the value. |
 | <span class="prop-name">scope</span> | <span class="prop-type">string |   | Set scope attribute. |
 | <span class="prop-name">sortDirection</span> | <span class="prop-type">enum:&nbsp;'asc'&nbsp;&#124;<br>&nbsp;'desc'&nbsp;&#124;<br>&nbsp;false<br> |   | Set aria-sort direction. |
 | <span class="prop-name">variant</span> | <span class="prop-type">enum:&nbsp;'head'&nbsp;&#124;<br>&nbsp;'body'&nbsp;&#124;<br>&nbsp;'footer'<br> |   | Specify the cell type. By default, the TableHead, TableBody or TableFooter parent component set the value. |
diff --git a/pages/api/table-row.md b/pages/api/table-row.md
--- a/pages/api/table-row.md
+++ b/pages/api/table-row.md
@@ -33,10 +33,10 @@ This property accepts the following keys:
 | Name | Description |
 |:-----|:------------|
 | <span class="prop-name">root</span> | Styles applied to the root element.
-| <span class="prop-name">selected</span> | Styles applied to the root element if `context.table` & `selected={true}`.
-| <span class="prop-name">hover</span> | Styles applied to the root element if `context.table` & `hover={true}`.
-| <span class="prop-name">head</span> | Styles applied to the root element if `context.table.head`.
-| <span class="prop-name">footer</span> | Styles applied to the root element if `context.table.footer`.
+| <span class="prop-name">selected</span> | Styles applied to the root element if `selected={true}`.
+| <span class="prop-name">hover</span> | Styles applied to the root element if `hover={true}`.
+| <span class="prop-name">head</span> | Styles applied to the root element if table variant = 'head'.
+| <span class="prop-name">footer</span> | Styles applied to the root element if table variant = 'footer'.
 
 Have a look at [overriding with classes](/customization/overrides#overriding-with-classes) section
 and the [implementation of the component](https://github.com/mui-org/material-ui/tree/master/packages/material-ui/src/TableRow/TableRow.js)
diff --git a/pages/api/table.md b/pages/api/table.md
--- a/pages/api/table.md
+++ b/pages/api/table.md
@@ -18,6 +18,7 @@ title: Table API
 | <span class="prop-name required">children *</span> | <span class="prop-type">node |   | The content of the table, normally `TableHeader` and `TableBody`. |
 | <span class="prop-name">classes</span> | <span class="prop-type">object |   | Override or extend the styles applied to the component. See [CSS API](#css-api) below for more details. |
 | <span class="prop-name">component</span> | <span class="prop-type">union:&nbsp;string&nbsp;&#124;<br>&nbsp;func&nbsp;&#124;<br>&nbsp;object<br> | <span class="prop-default">'table'</span> | The component used for the root node. Either a string to use a DOM element or a component. |
+| <span class="prop-name">padding</span> | <span class="prop-type">enum:&nbsp;'default'&nbsp;&#124;<br>&nbsp;'checkbox'&nbsp;&#124;<br>&nbsp;'dense'&nbsp;&#124;<br>&nbsp;'none'<br> | <span class="prop-default">'default'</span> | Allows TableCells to inherit padding of the Table. |
 
 Any other properties supplied will be spread to the root element (native element).
 


Test patch:
diff --git a/packages/material-ui/src/Table/Table.test.js b/packages/material-ui/src/Table/Table.test.js
--- a/packages/material-ui/src/Table/Table.test.js
+++ b/packages/material-ui/src/Table/Table.test.js
@@ -25,7 +25,7 @@ describe('<Table />', () => {
   it('should spread custom props on the root node', () => {
     const wrapper = shallow(<Table data-my-prop="woofTable">foo</Table>);
     assert.strictEqual(
-      wrapper.prop('data-my-prop'),
+      wrapper.props()['data-my-prop'],
       'woofTable',
       'custom prop should be woofTable',
     );
@@ -45,6 +45,8 @@ describe('<Table />', () => {
 
   it('should define table in the child context', () => {
     const wrapper = shallow(<Table>foo</Table>);
-    assert.deepStrictEqual(wrapper.instance().getChildContext().table, {});
+    assert.deepStrictEqual(wrapper.instance().getChildContext().table, {
+      padding: 'default',
+    });
   });
 });
diff --git a/packages/material-ui/src/TableBody/TableBody.test.js b/packages/material-ui/src/TableBody/TableBody.test.js
--- a/packages/material-ui/src/TableBody/TableBody.test.js
+++ b/packages/material-ui/src/TableBody/TableBody.test.js
@@ -38,6 +38,6 @@ describe('<TableBody />', () => {
 
   it('should define table.body in the child context', () => {
     const wrapper = shallow(<TableBody>foo</TableBody>);
-    assert.strictEqual(wrapper.instance().getChildContext().table.body, true);
+    assert.strictEqual(wrapper.instance().getChildContext().tablelvl2.variant, 'body');
   });
 });
diff --git a/packages/material-ui/src/TableCell/TableCell.test.js b/packages/material-ui/src/TableCell/TableCell.test.js
--- a/packages/material-ui/src/TableCell/TableCell.test.js
+++ b/packages/material-ui/src/TableCell/TableCell.test.js
@@ -66,7 +66,7 @@ describe('<TableCell />', () => {
 
   it('should render a th with the head class when in the context of a table head', () => {
     const wrapper = shallow(<TableCell />);
-    wrapper.setContext({ table: { head: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'head' } });
     assert.strictEqual(wrapper.name(), 'th');
     assert.strictEqual(wrapper.hasClass(classes.root), true);
     assert.strictEqual(wrapper.hasClass(classes.head), true, 'should have the head class');
@@ -75,13 +75,13 @@ describe('<TableCell />', () => {
 
   it('should render specified scope attribute even when in the context of a table head', () => {
     const wrapper = shallow(<TableCell scope="row" />);
-    wrapper.setContext({ table: { head: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'head' } });
     assert.strictEqual(wrapper.props().scope, 'row', 'should have the specified scope attribute');
   });
 
   it('should render a th with the footer class when in the context of a table footer', () => {
     const wrapper = shallow(<TableCell />);
-    wrapper.setContext({ table: { footer: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'footer' } });
     assert.strictEqual(wrapper.name(), 'td');
     assert.strictEqual(wrapper.hasClass(classes.root), true);
     assert.strictEqual(wrapper.hasClass(classes.footer), true, 'should have the footer class');
@@ -95,33 +95,33 @@ describe('<TableCell />', () => {
 
   it('should render with the footer class when in the context of a table footer', () => {
     const wrapper = shallow(<TableCell />);
-    wrapper.setContext({ table: { footer: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'footer' } });
     assert.strictEqual(wrapper.hasClass(classes.root), true);
     assert.strictEqual(wrapper.hasClass(classes.footer), true, 'should have the footer class');
   });
 
   it('should render with the head class when variant is head, overriding context', () => {
     const wrapper = shallow(<TableCell variant="head" />);
-    wrapper.setContext({ table: { footer: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'footer' } });
     assert.strictEqual(wrapper.hasClass(classes.head), true);
     assert.strictEqual(wrapper.props().scope, undefined, 'should have the correct scope attribute');
   });
 
   it('should render without head class when variant is body, overriding context', () => {
     const wrapper = shallow(<TableCell variant="body" />);
-    wrapper.setContext({ table: { head: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'head' } });
     assert.strictEqual(wrapper.hasClass(classes.head), false);
   });
 
   it('should render without footer class when variant is body, overriding context', () => {
     const wrapper = shallow(<TableCell variant="body" />);
-    wrapper.setContext({ table: { footer: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'footer' } });
     assert.strictEqual(wrapper.hasClass(classes.footer), false);
   });
 
   it('should render with the footer class when variant is footer, overriding context', () => {
     const wrapper = shallow(<TableCell variant="footer" />);
-    wrapper.setContext({ table: { head: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'head' } });
     assert.strictEqual(wrapper.hasClass(classes.footer), true);
   });
 
diff --git a/packages/material-ui/src/TableHead/TableHead.test.js b/packages/material-ui/src/TableHead/TableHead.test.js
--- a/packages/material-ui/src/TableHead/TableHead.test.js
+++ b/packages/material-ui/src/TableHead/TableHead.test.js
@@ -36,6 +36,6 @@ describe('<TableHead />', () => {
 
   it('should define table.head in the child context', () => {
     const wrapper = shallow(<TableHead>foo</TableHead>);
-    assert.strictEqual(wrapper.instance().getChildContext().table.head, true);
+    assert.strictEqual(wrapper.instance().getChildContext().tablelvl2.variant, 'head');
   });
 });
diff --git a/packages/material-ui/src/TableRow/TableRow.test.js b/packages/material-ui/src/TableRow/TableRow.test.js
--- a/packages/material-ui/src/TableRow/TableRow.test.js
+++ b/packages/material-ui/src/TableRow/TableRow.test.js
@@ -47,14 +47,14 @@ describe('<TableRow />', () => {
 
   it('should render with the head class when in the context of a table head', () => {
     const wrapper = shallow(<TableRow />);
-    wrapper.setContext({ table: { head: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'head' } });
     assert.strictEqual(wrapper.hasClass(classes.root), true);
     assert.strictEqual(wrapper.hasClass(classes.head), true, 'should have the head class');
   });
 
   it('should render with the footer class when in the context of a table footer', () => {
     const wrapper = shallow(<TableRow />);
-    wrapper.setContext({ table: { footer: true } });
+    wrapper.setContext({ tablelvl2: { variant: 'footer' } });
     assert.strictEqual(wrapper.hasClass(classes.root), true);
     assert.strictEqual(wrapper.hasClass(classes.footer), true, 'should have the footer class');
   });