Skip to content

Commit fc73b3c

Browse files
Issue 118 width behavior (#129)
* add width percentage support + content_style * fix visual regression with empty df * only apply row style when necessary
1 parent 3a76f3f commit fc73b3c

File tree

14 files changed

+164
-53
lines changed

14 files changed

+164
-53
lines changed

packages/dash-table/CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,15 @@
188188
Issue: https://github.com/plotly/dash-table/issues/91
189189

190190
Sorting arrow will no longer highlight.
191+
192+
## RC23 - Width percentage
193+
194+
Columns can now accept '%' width, minWidth, maxWidth.
195+
196+
For the percentages to have meaning, the dash-table must be forced to have a width and the content of the dash-table must be forced to grow to fill the available space made available by the container (by default the table is only as big as it needs to be).
197+
198+
Added prop content_style that takes values 'fit' or 'grow' (Default='fit')
199+
200+
1. Add the prop content_style='grow' to make the table fill its space, this will make sure the % are applied to all the space available.
201+
2. Add the following selector (with the proper values) to the table_style prop
202+
{ selector: '.dash-spreadsheet', rule: 'width: 100%; max-width: 100%' }

packages/dash-table/dash_table/bundle.js

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dash-table/dash_table/demo.js

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dash-table/dash_table/metadata.json

+21
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,27 @@
2929
"computed": false
3030
}
3131
},
32+
"content_style": {
33+
"type": {
34+
"name": "enum",
35+
"value": [
36+
{
37+
"value": "'fit'",
38+
"computed": false
39+
},
40+
{
41+
"value": "'grow'",
42+
"computed": false
43+
}
44+
]
45+
},
46+
"required": false,
47+
"description": "",
48+
"defaultValue": {
49+
"value": "'fit'",
50+
"computed": false
51+
}
52+
},
3253
"dataframe": {
3354
"type": {
3455
"name": "arrayOf",

packages/dash-table/dash_table/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dash-table",
3-
"version": "3.0.0rc22",
3+
"version": "3.0.0rc23",
44
"description": "Dash table",
55
"main": "build/index.js",
66
"scripts": {

packages/dash-table/demo/App.js

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class App extends Component {
2525
deletable: true,
2626
// type: 'dropdown'
2727
})),
28+
content_style: 'grow',
2829
editable: true,
2930
sorting: true,
3031
n_fixed_rows: 4,

packages/dash-table/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dash-table",
3-
"version": "3.0.0rc22",
3+
"version": "3.0.0rc23",
44
"description": "Dash table",
55
"main": "build/index.js",
66
"scripts": {

packages/dash-table/src/dash-table/Table.js

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const defaultProps = {
2020
},
2121
navigation: 'page',
2222

23+
content_style: 'fit',
2324
filtering: false,
2425
filtering_settings: '',
2526
filtering_type: 'basic',
@@ -87,6 +88,7 @@ export const defaultProps = {
8788
export const propTypes = {
8889
active_cell: PropTypes.array,
8990
columns: PropTypes.arrayOf(PropTypes.object),
91+
content_style: PropTypes.oneOf(['fit', 'grow']),
9092

9193
dataframe: PropTypes.arrayOf(PropTypes.object),
9294
dataframe_previous: PropTypes.arrayOf(PropTypes.object),

packages/dash-table/src/dash-table/components/ControlledTable/index.tsx

+50-19
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ export default class ControlledTable extends Component<ControlledTableProps> {
7474
this.props.setProps({ active_cell: this.props.selected_cell[0] });
7575
}
7676

77+
this.handleResize();
78+
}
79+
80+
componentWillMount() {
7781
// Fallback method for paste handling in Chrome
7882
// when no input element has focused inside the table
7983
window.addEventListener('resize', this.handleResize);
@@ -87,14 +91,6 @@ export default class ControlledTable extends Component<ControlledTableProps> {
8791
document.removeEventListener('paste', this.handlePaste);
8892
}
8993

90-
componentWillUpdate() {
91-
const { table_style } = this.props;
92-
93-
R.forEach(({ selector, rule }) => {
94-
this.stylesheet.setRule(selector, rule);
95-
}, table_style);
96-
}
97-
9894
componentDidUpdate() {
9995
this.handleResize();
10096
this.handleDropdown();
@@ -128,6 +124,12 @@ export default class ControlledTable extends Component<ControlledTableProps> {
128124
handleResize = () => {
129125
const { r0c0, r0c1, r1c0, r1c1 } = this.refs as { [key: string]: HTMLElement };
130126

127+
const { n_fixed_columns, n_fixed_rows, table_style } = this.props;
128+
129+
R.forEach(({ selector, rule }) => {
130+
this.stylesheet.setRule(selector, rule);
131+
}, table_style);
132+
131133
// Adjust [fixed columns/fixed rows combo] to fixed rows height
132134
let trs = r0c1.querySelectorAll('tr');
133135
r0c0.querySelectorAll('tr').forEach((tr, index) => {
@@ -152,6 +154,28 @@ export default class ControlledTable extends Component<ControlledTableProps> {
152154

153155
this.stylesheet.setRule('.cell-1-0 tr', `height: ${getComputedStyle(contentTr).height}`);
154156
}
157+
158+
// Adjust the width of the fixed row header
159+
if (n_fixed_rows) {
160+
r1c1.querySelectorAll('tr:first-of-type td').forEach((td, index) => {
161+
const width: any = getComputedStyle(td).width;
162+
this.stylesheet.setRule(
163+
`.dash-fixed-row:not(.dash-fixed-column) th:nth-of-type(${index + 1})`,
164+
`width: ${width}; min-width: ${width}; max-width: ${width};`
165+
);
166+
});
167+
}
168+
169+
// Adjust the width of the fixed row / fixed columns header
170+
if (n_fixed_columns && n_fixed_rows) {
171+
r1c0.querySelectorAll('tr:first-of-type td').forEach((td, index) => {
172+
const width: any = getComputedStyle(td).width;
173+
this.stylesheet.setRule(
174+
`.dash-fixed-column.dash-fixed-row th:nth-of-type(${index + 1})`,
175+
`width: ${width}; min-width: ${width}; max-width: ${width};`
176+
);
177+
});
178+
}
155179
}
156180

157181
get $el() {
@@ -594,10 +618,6 @@ export default class ControlledTable extends Component<ControlledTableProps> {
594618
`.dash-spreadsheet-inner td.column-${typeIndex}`,
595619
`width: 30px; max-width: 30px; min-width: 30px;`
596620
);
597-
this.stylesheet.setRule(
598-
`.dash-spreadsheet-inner th.column-${typeIndex}`,
599-
`width: 30px; max-width: 30px; min-width: 30px;`
600-
);
601621

602622
++typeIndex;
603623
}
@@ -607,10 +627,6 @@ export default class ControlledTable extends Component<ControlledTableProps> {
607627
`.dash-spreadsheet-inner td.column-${typeIndex}`,
608628
`width: 30px; max-width: 30px; min-width: 30px;`
609629
);
610-
this.stylesheet.setRule(
611-
`.dash-spreadsheet-inner th.column-${typeIndex}`,
612-
`width: 30px; max-width: 30px; min-width: 30px;`
613-
);
614630

615631
++typeIndex;
616632
}
@@ -624,6 +640,7 @@ export default class ControlledTable extends Component<ControlledTableProps> {
624640
`.dash-spreadsheet-inner td.column-${typeIndex}`,
625641
`width: ${width}; max-width: ${maxWidth}; min-width: ${minWidth};`
626642
);
643+
627644
this.stylesheet.setRule(
628645
`.dash-spreadsheet-inner th.column-${typeIndex}`,
629646
`width: ${width}; max-width: ${maxWidth}; min-width: ${minWidth};`
@@ -707,6 +724,7 @@ export default class ControlledTable extends Component<ControlledTableProps> {
707724
const {
708725
id,
709726
columns,
727+
content_style,
710728
n_fixed_columns,
711729
n_fixed_rows,
712730
row_deletable,
@@ -719,14 +737,27 @@ export default class ControlledTable extends Component<ControlledTableProps> {
719737
'dash-spreadsheet-inner',
720738
'dash-spreadsheet',
721739
...(n_fixed_rows ? ['freeze-top'] : []),
722-
...(n_fixed_columns ? ['freeze-left'] : [])
740+
...(n_fixed_columns ? ['freeze-left'] : []),
741+
[`dash-${content_style}`]
723742
];
724743

725744
const containerClasses = [
726745
'dash-spreadsheet',
727746
'dash-spreadsheet-container',
728747
...(n_fixed_rows ? ['freeze-top'] : []),
729-
...(n_fixed_columns ? ['freeze-left'] : [])
748+
...(n_fixed_columns ? ['freeze-left'] : []),
749+
[`dash-${content_style}`]
750+
];
751+
752+
const fragmentClasses = [
753+
[
754+
n_fixed_rows && n_fixed_columns ? 'dash-fixed-row dash-fixed-column' : '',
755+
n_fixed_rows ? 'dash-fixed-row' : ''
756+
],
757+
[
758+
n_fixed_columns ? 'dash-fixed-column' : '',
759+
'dash-fixed-content'
760+
]
730761
];
731762

732763
const cells = this.getCells();
@@ -748,7 +779,7 @@ export default class ControlledTable extends Component<ControlledTableProps> {
748779
>{row.map((cell, columnIndex) => (<div
749780
key={columnIndex}
750781
ref={`r${rowIndex}c${columnIndex}`}
751-
className={`cell cell-${rowIndex}-${columnIndex}`}
782+
className={`cell cell-${rowIndex}-${columnIndex} ${fragmentClasses[rowIndex][columnIndex]}`}
752783
>{cell}</div>))
753784
}</div>))}
754785
</div>

packages/dash-table/src/dash-table/components/HeaderFactory.tsx

+1-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22
import * as R from 'ramda';
33

4-
import Stylesheet from 'core/Stylesheet';
54
import { SortDirection, SortSettings } from 'core/sorting';
65
import multiUpdateSettings from 'core/sorting/multi';
76
import singleUpdateSettings from 'core/sorting/single';
@@ -117,7 +116,6 @@ export default class HeaderFactory {
117116
columnRowIndex,
118117
labels,
119118
mergeCells,
120-
n_fixed_columns,
121119
offset,
122120
rowSorting,
123121
virtualization
@@ -141,16 +139,12 @@ export default class HeaderFactory {
141139
});
142140
}
143141

144-
const visibleColumns = columns.filter(column => !column.hidden);
145-
146142
return R.filter(column => !!column, columnIndices.map((columnId, spanId) => {
147143
const c = columns[columnId];
148144
if (c.hidden) {
149145
return null;
150146
}
151147

152-
const visibleIndex = visibleColumns.indexOf(c) + offset;
153-
154148
let colSpan: number;
155149
if (!mergeCells) {
156150
colSpan = 1;
@@ -166,39 +160,21 @@ export default class HeaderFactory {
166160
}
167161
}
168162

169-
// This is not efficient and can be improved upon...
170-
// Fixed columns need to override the default cell behavior when they span multiple columns
171-
// Find all columns that fit the header's range [index, index+colspan[ and keep the fixed/visible ones
172-
const visibleColumnId = visibleColumns.indexOf(c);
173-
174-
const spannedColumns = visibleColumns.filter((column, index) =>
175-
!column.hidden &&
176-
index >= visibleColumnId &&
177-
index < visibleColumnId + colSpan &&
178-
index + offset < n_fixed_columns
179-
);
180-
181-
// Calculate the width of all those columns combined
182-
const width = `calc(${spannedColumns.map(column => Stylesheet.unit(column.width || DEFAULT_CELL_WIDTH, 'px')).join(' + ')})`;
183-
const maxWidth = `calc(${spannedColumns.map(column => Stylesheet.unit(column.maxWidth || column.width || DEFAULT_CELL_WIDTH, 'px')).join(' + ')})`;
184-
const minWidth = `calc(${spannedColumns.map(column => Stylesheet.unit(column.minWidth || column.width || DEFAULT_CELL_WIDTH, 'px')).join(' + ')})`;
185-
186163
return (<th
187164
key={`header-cell-${columnId}`}
188165
colSpan={colSpan}
189166
className={
190167
`column-${columnId + offset} ` +
191168
(columnId === columns.length - 1 || columnId === R.last(columnIndices) ? 'cell--right-last ' : '')
192169
}
193-
style={visibleIndex < n_fixed_columns ? { maxWidth, minWidth, width } : undefined}
194170
>
195171
{rowSorting ? (
196172
<span
197173
className='sort'
198174
onClick={HeaderFactory.doSort(c.id, options)}
199175
>
200176
{HeaderFactory.getSortingIcon(c.id, options)}
201-
</span>) : ('')
177+
</span>) : ''
202178
}
203179

204180
{((c.editable_name && R.type(c.editable_name) === 'Boolean') ||

packages/dash-table/src/dash-table/components/Table/Table.less

+11
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@
150150
}
151151
}
152152

153+
&.dash-grow {
154+
.cell-0-1,
155+
.cell-1-1 {
156+
flex: 1 0 auto;
157+
}
158+
159+
table {
160+
width: 100%;
161+
}
162+
}
163+
153164
&:not(.freeze-top):not(.freeze-left) {
154165
.cell-1-1 {
155166
.top-left-cells();

packages/dash-table/src/dash-table/components/Table/props.ts

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export enum FilteringType {
66
Basic = 'basic'
77
}
88

9+
export enum ContentStyle {
10+
Fit = 'fit',
11+
Grow = 'grow'
12+
}
13+
914
export type ActiveCell = CellCoordinates | [];
1015
export type CellCoordinates = [number, number];
1116
export type ColumnId = string | number;
@@ -63,6 +68,7 @@ interface IProps {
6368
column_conditional_styles?: any[];
6469
column_static_dropdown?: any;
6570
column_static_style?: any;
71+
content_style: ContentStyle;
6672
dataframe?: Dataframe;
6773
dropdown_properties: any; // legacy
6874
editable?: boolean;

packages/dash-table/tests/visual/percy-storybook/DashTable.percy.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import fixtures from './fixtures';
88
const setProps = () => { };
99

1010
// Legacy: Tests previously run in Python
11-
const fixtureStories = storiesOf('DashTable/Fixtures');
11+
const fixtureStories = storiesOf('DashTable/Fixtures', module);
1212
fixtures.forEach(fixture => fixtureStories.add(fixture.name, () => (<DashTable {...Object.assign(fixture.props)} />)));
1313

1414
storiesOf('DashTable/Without Data', module)

0 commit comments

Comments
 (0)