Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Table] Add truncation controls to example page #1253

Merged
merged 17 commits into from
Jun 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/table/preview/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@
color: #A7B6C2;
}

.sidebar label:last-child {
margin-bottom: 0;
.sidebar-indented-group {
margin-left: 15px;
}

label.tbl-select-label {
Expand Down Expand Up @@ -126,6 +126,10 @@
border: 1px solid #DB3737;
background-color: rgba(219, 55, 55, 0.1);
}

.tbl-select-label.pt-disabled {
cursor: not-allowed;
}
</style>
</head>
<body>
Expand Down
160 changes: 136 additions & 24 deletions packages/table/preview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as React from "react";
import * as ReactDOM from "react-dom";

import {
Classes,
FocusStyleManager,
Menu,
MenuDivider,
Expand All @@ -31,6 +32,8 @@ import {
RowHeaderCell,
Table,
TableLoadingOption,
TruncatedFormat,
TruncatedPopoverMode,
Utils,
} from "../src/index";

Expand All @@ -51,8 +54,10 @@ type IMutableStateUpdateCallback =

interface IMutableTableState {
cellContent?: CellContent;
cellTruncatedPopoverMode?: TruncatedPopoverMode;
enableCellEditing?: boolean;
enableCellSelection?: boolean;
enableCellTruncation?: boolean;
enableColumnNameEditing?: boolean;
enableColumnReordering?: boolean;
enableColumnResizing?: boolean;
Expand All @@ -65,14 +70,14 @@ interface IMutableTableState {
enableRowSelection?: boolean;
numCols?: number;
numRows?: number;
selectedFocusStyle?: FocusStyle;
showCallbackLogs?: boolean;
showCellsLoading?: boolean;
showColumnHeadersLoading?: boolean;
showColumnInteractionBar?: boolean;
showColumnMenus?: boolean;
showCustomRegions?: boolean;
showFocusCell?: boolean;
selectedFocusStyle?: FocusStyle;
showGhostCells?: boolean;
showInline?: boolean;
showRowHeaders?: boolean;
Expand Down Expand Up @@ -110,6 +115,12 @@ const CELL_CONTENTS = [
CellContent.LONG_TEXT,
];

const TRUNCATED_POPOVER_MODES = [
TruncatedPopoverMode.ALWAYS,
TruncatedPopoverMode.NEVER,
TruncatedPopoverMode.WHEN_TRUNCATED,
] as TruncatedPopoverMode[];

const COLUMN_COUNT_DEFAULT_INDEX = 2;
const ROW_COUNT_DEFAULT_INDEX = 3;

Expand All @@ -136,8 +147,10 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
super(props, context);
this.state = {
cellContent: CellContent.CELL_NAMES,
cellTruncatedPopoverMode: TruncatedPopoverMode.WHEN_TRUNCATED,
enableCellEditing: true,
enableCellSelection: true,
enableCellTruncation: false,
enableColumnNameEditing: true,
enableColumnReordering: true,
enableColumnResizing: true,
Expand All @@ -162,6 +175,7 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
showInline: false,
showRowHeaders: true,
showRowHeadersLoading: false,
showZebraStriping: false,
};
}

Expand Down Expand Up @@ -221,6 +235,7 @@ class MutableTable extends React.Component<{}, IMutableTableState> {

public componentDidUpdate() {
this.syncFocusStyle();
this.syncDependentBooleanStates();
}

// Renderers
Expand Down Expand Up @@ -333,20 +348,36 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
const isEvenRow = rowIndex % 2 === 0;
const classes = classNames({ "tbl-zebra-stripe": this.state.showZebraStriping && isEvenRow });

return this.state.enableCellEditing ? (
<EditableCell
if (this.state.enableCellEditing) {
return <EditableCell
className={classes}
columnIndex={columnIndex}
loading={this.state.showCellsLoading}
onConfirm={this.handleEditableBodyCellConfirm}
rowIndex={rowIndex}
value={valueAsString}
/>
) : (
<Cell className={classes} columnIndex={columnIndex} rowIndex={rowIndex}>
{valueAsString}
</Cell>
);
/>;
} else if (this.state.enableCellTruncation) {
return (
<Cell className={classes}>
<TruncatedFormat
detectTruncation={true}
preformatted={false}
showPopover={this.state.cellTruncatedPopoverMode}
truncateLength={80}
truncationSuffix="..."
>
{valueAsString}
</TruncatedFormat>
</Cell>
);
} else {
return (
<Cell className={classes} columnIndex={columnIndex} rowIndex={rowIndex}>
{valueAsString}
</Cell>
);
}
}

private renderSidebar() {
Expand All @@ -357,6 +388,16 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
this.toCellContentLabel,
this.handleNumberStateChange,
);
const truncatedPopoverModeMenu = this.renderSelectMenu(
"Popover",
"cellTruncatedPopoverMode",
TRUNCATED_POPOVER_MODES,
this.toTruncatedPopoverModeLabel,
this.handleNumberStateChange,
"enableCellTruncation",
true,
);

return (
<div className="sidebar pt-elevation-0">
<h4>Table</h4>
Expand Down Expand Up @@ -401,6 +442,11 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
<h6>Interactions</h6>
{this.renderSwitch("Editing", "enableCellEditing")}
{this.renderSwitch("Selection", "enableCellSelection")}
{this.renderSwitch("Truncation", "enableCellTruncation", "enableCellEditing", false)}

<div className="sidebar-indented-group">
{truncatedPopoverModeMenu}
</div>

<h4>Page</h4>
<h6>Display</h6>
Expand All @@ -409,23 +455,35 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
);
}

private renderSwitch(label: string, stateKey: keyof IMutableTableState) {
return (
<Switch
checked={this.state[stateKey] as boolean}
className="pt-align-right"
label={label}
onChange={this.handleBooleanStateChange(stateKey)}
/>
);
private renderSwitch(
label: string,
stateKey: keyof IMutableTableState,
prereqStateKey?: keyof IMutableTableState,
prereqStateKeyValue?: any,
) {
const isDisabled = !this.isPrereqStateKeySatisfied(prereqStateKey, prereqStateKeyValue);

const child = <Switch
checked={this.state[stateKey] as boolean}
className={Classes.ALIGN_RIGHT}
disabled={isDisabled}
label={label}
onChange={this.handleBooleanStateChange(stateKey)}
/>;

if (isDisabled) {
return this.wrapDisabledControlWithTooltip(child, prereqStateKey, prereqStateKeyValue);
} else {
return child;
}
}

private renderFocusStyleSelectMenu() {
const { selectedFocusStyle } = this.state;
return (
<label className="pt-label pt-inline tbl-select-label">
<label className={classNames(Classes.LABEL, Classes.INLINE, "tbl-select-label")}>
{"Focus outlines"}
<div className="pt-select">
<div className={Classes.SELECT}>
<select onChange={this.updateFocusStyleState()} value={selectedFocusStyle}>
<option value={"tab"}>
On tab
Expand Down Expand Up @@ -455,7 +513,11 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
values: T[],
generateValueLabel: (value: any) => string,
handleChange: IMutableStateUpdateCallback,
prereqStateKey?: keyof IMutableTableState,
prereqStateKeyValue?: any,
) {
const isDisabled = !this.isPrereqStateKeySatisfied(prereqStateKey, prereqStateKeyValue);

// need to explicitly cast generic type T to string
const selectedValue = this.state[stateKey].toString();
const options = values.map((value) => {
Expand All @@ -465,16 +527,47 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
</option>
);
});
return (
<label className="pt-label pt-inline tbl-select-label">

const labelClasses = classNames(Classes.LABEL, Classes.INLINE, "tbl-select-label", {
[Classes.DISABLED]: isDisabled,
});

const child = (
<label className={labelClasses}>
{label}
<div className="pt-select">
<select onChange={handleChange(stateKey)} value={selectedValue}>
<div className={Classes.SELECT}>
<select onChange={handleChange(stateKey)} value={selectedValue} disabled={isDisabled}>
{options}
</select>
</div>
</label>
);

if (isDisabled) {
return this.wrapDisabledControlWithTooltip(child, prereqStateKey, prereqStateKeyValue);
} else {
return child;
}
}

// Disabled control helpers
// ========================

private isPrereqStateKeySatisfied(key?: keyof IMutableTableState, value?: any) {
return key == null || this.state[key] === value;
}

private wrapDisabledControlWithTooltip(
element: JSX.Element,
prereqStateKey: keyof IMutableTableState,
prereqStateKeyValue: any,
) {
// Blueprint Tooltip affects the layout, so just show a native title on hover
return (
<div title={`Requires ${prereqStateKey}=${prereqStateKeyValue}`}>
{element}
</div>
);
}

// Select menu - label generators
Expand All @@ -493,6 +586,19 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
}
}

private toTruncatedPopoverModeLabel(truncatedPopoverMode: TruncatedPopoverMode) {
switch (truncatedPopoverMode) {
case TruncatedPopoverMode.ALWAYS:
return "Always";
case TruncatedPopoverMode.NEVER:
return "Never";
case TruncatedPopoverMode.WHEN_TRUNCATED:
return "When truncated";
default:
return "";
}
}

private toValueLabel(value: any) {
return value.toString();
}
Expand Down Expand Up @@ -569,6 +675,12 @@ class MutableTable extends React.Component<{}, IMutableTableState> {
}
}

private syncDependentBooleanStates = () => {
if (this.state.enableCellEditing && this.state.enableCellTruncation) {
this.setState({ enableCellTruncation: false });
}
}

private handleBooleanStateChange = (stateKey: keyof IMutableTableState) => {
return handleBooleanChange((value) => this.setState({ [stateKey]: value }));
}
Expand Down