Skip to content
Snippets Groups Projects
Unverified Commit 971c5053 authored by Aleksandr Lesnenko's avatar Aleksandr Lesnenko Committed by GitHub
Browse files

Pivot table conditional formatting (#24930)

* pivot table conditional formatting

* tweak styles

* hide highlight row setting

* adjust styles

* specs
parent d87f21e5
No related branches found
No related tags found
No related merge requests found
import _ from "underscore"; import _ from "underscore";
import { getIn } from "icepick"; import { getIn } from "icepick";
import { t } from "ttag"; import { t } from "ttag";
import { makeCellBackgroundGetter } from "metabase/visualizations/lib/table_format";
import { formatValue, formatColumn } from "metabase/lib/formatting"; import { formatValue, formatColumn } from "metabase/lib/formatting";
...@@ -38,12 +39,7 @@ export function multiLevelPivot(data, settings) { ...@@ -38,12 +39,7 @@ export function multiLevelPivot(data, settings) {
.filter(index => index !== -1), .filter(index => index !== -1),
); );
const { pivotData, columns } = splitPivotData( const { pivotData, columns } = splitPivotData(data);
data,
rowColumnIndexes,
columnColumnIndexes,
);
const columnSettings = columns.map(column => settings.column(column)); const columnSettings = columns.map(column => settings.column(column));
const allCollapsedSubtotals = settings[COLLAPSED_ROWS_SETTING].value; const allCollapsedSubtotals = settings[COLLAPSED_ROWS_SETTING].value;
const collapsedSubtotals = filterCollapsedSubtotals( const collapsedSubtotals = filterCollapsedSubtotals(
...@@ -83,8 +79,13 @@ export function multiLevelPivot(data, settings) { ...@@ -83,8 +79,13 @@ export function multiLevelPivot(data, settings) {
columnColumnIndexes.concat(rowColumnIndexes).map(index => row[index]), columnColumnIndexes.concat(rowColumnIndexes).map(index => row[index]),
); );
const values = valueColumnIndexes.map(index => row[index]); const values = valueColumnIndexes.map(index => row[index]);
const valueColumns = valueColumnIndexes.map(
index => columnSettings[index]?.column,
);
valuesByKey[valueKey] = { valuesByKey[valueKey] = {
values, values,
valueColumns,
data: row.map((value, index) => ({ value, col: columns[index] })), data: row.map((value, index) => ({ value, col: columns[index] })),
dimensions: row dimensions: row
.map((value, index) => ({ .map((value, index) => ({
...@@ -180,6 +181,12 @@ export function multiLevelPivot(data, settings) { ...@@ -180,6 +181,12 @@ export function multiLevelPivot(data, settings) {
const leftHeaderItems = treeToArray(formattedRowTree.flat()); const leftHeaderItems = treeToArray(formattedRowTree.flat());
const topHeaderItems = treeToArray(formattedColumnTree.flat()); const topHeaderItems = treeToArray(formattedColumnTree.flat());
const colorGetter = makeCellBackgroundGetter(
pivotData[primaryRowsKey],
columns,
settings,
);
const getRowSection = createRowSectionGetter({ const getRowSection = createRowSectionGetter({
valuesByKey, valuesByKey,
subtotalValues, subtotalValues,
...@@ -188,6 +195,7 @@ export function multiLevelPivot(data, settings) { ...@@ -188,6 +195,7 @@ export function multiLevelPivot(data, settings) {
rowColumnIndexes, rowColumnIndexes,
columnIndex, columnIndex,
rowIndex, rowIndex,
colorGetter,
}); });
return { return {
...@@ -206,7 +214,7 @@ export function multiLevelPivot(data, settings) { ...@@ -206,7 +214,7 @@ export function multiLevelPivot(data, settings) {
// This pulls apart the different aggregations that were packed into one result set. // This pulls apart the different aggregations that were packed into one result set.
// There's a column indicating which breakouts were used to compute that row. // There's a column indicating which breakouts were used to compute that row.
// We use that column to split apart the data and convert the field refs to indexes. // We use that column to split apart the data and convert the field refs to indexes.
function splitPivotData(data, rowIndexes, columnIndexes) { function splitPivotData(data) {
const groupIndex = data.cols.findIndex(isPivotGroupColumn); const groupIndex = data.cols.findIndex(isPivotGroupColumn);
const columns = data.cols.filter(col => !isPivotGroupColumn(col)); const columns = data.cols.filter(col => !isPivotGroupColumn(col));
const breakouts = columns.filter(col => col.source === "breakout"); const breakouts = columns.filter(col => col.source === "breakout");
...@@ -260,6 +268,7 @@ function createRowSectionGetter({ ...@@ -260,6 +268,7 @@ function createRowSectionGetter({
rowColumnIndexes, rowColumnIndexes,
columnIndex, columnIndex,
rowIndex, rowIndex,
colorGetter,
}) { }) {
const formatValues = values => const formatValues = values =>
values === undefined values === undefined
...@@ -292,10 +301,20 @@ function createRowSectionGetter({ ...@@ -292,10 +301,20 @@ function createRowSectionGetter({
const otherAttrs = rowValues.length === 0 ? { isGrandTotal: true } : {}; const otherAttrs = rowValues.length === 0 ? { isGrandTotal: true } : {};
return getSubtotals(indexes, indexValues, otherAttrs); return getSubtotals(indexes, indexValues, otherAttrs);
} }
const { values, data, dimensions } = const { values, data, dimensions, valueColumns } =
valuesByKey[JSON.stringify(indexValues)] || {}; valuesByKey[JSON.stringify(indexValues)] || {};
return formatValues(values).map(o => return formatValues(values).map((o, index) =>
data === undefined ? o : { ...o, clicked: { data, dimensions } }, data === undefined
? o
: {
...o,
clicked: { data, dimensions },
backgroundColor: colorGetter(
values[index],
o.rowIndex,
valueColumns[index].name,
),
},
); );
}; };
return _.memoize(getter, (i1, i2) => [i1, i2].join()); return _.memoize(getter, (i1, i2) => [i1, i2].join());
......
...@@ -94,11 +94,12 @@ export default class ChartSettingsTableFormatting extends React.Component { ...@@ -94,11 +94,12 @@ export default class ChartSettingsTableFormatting extends React.Component {
editingRuleIsNew: null, editingRuleIsNew: null,
}; };
render() { render() {
const { value, onChange, cols } = this.props; const { value, onChange, cols, canHighlightRow } = this.props;
const { editingRule, editingRuleIsNew } = this.state; const { editingRule, editingRuleIsNew } = this.state;
if (editingRule !== null && value[editingRule]) { if (editingRule !== null && value[editingRule]) {
return ( return (
<RuleEditor <RuleEditor
canHighlightRow={canHighlightRow}
rule={value[editingRule]} rule={value[editingRule]}
cols={cols} cols={cols}
isNew={editingRuleIsNew} isNew={editingRuleIsNew}
...@@ -297,7 +298,15 @@ const RuleDescription = ({ rule }) => { ...@@ -297,7 +298,15 @@ const RuleDescription = ({ rule }) => {
); );
}; };
const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => { const RuleEditor = ({
rule,
cols,
isNew,
onChange,
onDone,
onRemove,
canHighlightRow = true,
}) => {
const selectedColumns = rule.columns.map(name => _.findWhere(cols, { name })); const selectedColumns = rule.columns.map(name => _.findWhere(cols, { name }));
const isStringRule = const isStringRule =
selectedColumns.length > 0 && _.all(selectedColumns, isString); selectedColumns.length > 0 && _.all(selectedColumns, isString);
...@@ -381,11 +390,16 @@ const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => { ...@@ -381,11 +390,16 @@ const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => {
colors={COLORS} colors={COLORS}
onChange={color => onChange({ ...rule, color })} onChange={color => onChange({ ...rule, color })}
/> />
<h3 className="mt3 mb1">{t`Highlight the whole row`}</h3> {canHighlightRow && (
<Toggle <>
value={rule.highlight_row} <h3 className="mt3 mb1">{t`Highlight the whole row`}</h3>
onChange={highlight_row => onChange({ ...rule, highlight_row })}
/> <Toggle
value={rule.highlight_row}
onChange={highlight_row => onChange({ ...rule, highlight_row })}
/>
</>
)}
</div> </div>
) : rule.type === "range" ? ( ) : rule.type === "range" ? (
<div> <div>
......
...@@ -6,9 +6,8 @@ import _ from "underscore"; ...@@ -6,9 +6,8 @@ import _ from "underscore";
import { getIn, updateIn } from "icepick"; import { getIn, updateIn } from "icepick";
import { Grid, Collection, ScrollSync, AutoSizer } from "react-virtualized"; import { Grid, Collection, ScrollSync, AutoSizer } from "react-virtualized";
import { darken, lighten } from "metabase/lib/colors";
import "metabase/visualizations/components/TableInteractive/TableInteractive.css";
import { getScrollBarSize } from "metabase/lib/dom"; import { getScrollBarSize } from "metabase/lib/dom";
import ChartSettingsTableFormatting from "metabase/visualizations/components/settings/ChartSettingsTableFormatting";
import Ellipsified from "metabase/core/components/Ellipsified"; import Ellipsified from "metabase/core/components/Ellipsified";
import Icon from "metabase/components/Icon"; import Icon from "metabase/components/Icon";
...@@ -27,23 +26,13 @@ import { columnSettings } from "metabase/visualizations/lib/settings/column"; ...@@ -27,23 +26,13 @@ import { columnSettings } from "metabase/visualizations/lib/settings/column";
import { findDOMNode } from "react-dom"; import { findDOMNode } from "react-dom";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { PLUGIN_SELECTORS } from "metabase/plugins"; import { PLUGIN_SELECTORS } from "metabase/plugins";
import { RowToggleIconRoot } from "./PivotTable.styled"; import {
PivotTableRoot,
const getBgLightColor = (hasCustomColors, isNightMode) => { PivotTableCell,
if (isNightMode) { PivotTableTopLeftCellsContainer,
return lighten("bg-black", 0.3); RowToggleIconRoot,
} CELL_HEIGHT,
} from "./PivotTable.styled";
return hasCustomColors ? darken("white", 0.01) : lighten("brand", 0.65);
};
const getBgDarkColor = (hasCustomColors, isNightMode) => {
if (isNightMode) {
return lighten("bg-black", 0.1);
}
return hasCustomColors ? darken("white", 0.035) : lighten("brand", 0.6);
};
const partitions = [ const partitions = [
{ {
...@@ -71,7 +60,6 @@ const partitions = [ ...@@ -71,7 +60,6 @@ const partitions = [
// cell width and height for normal body cells // cell width and height for normal body cells
const CELL_WIDTH = 100; const CELL_WIDTH = 100;
const CELL_HEIGHT = 25;
// the left header has a wider cell width and some additional spacing on the left to align with the title // the left header has a wider cell width and some additional spacing on the left to align with the title
const LEFT_HEADER_LEFT_SPACING = 24; const LEFT_HEADER_LEFT_SPACING = 24;
const LEFT_HEADER_CELL_WIDTH = 145; const LEFT_HEADER_CELL_WIDTH = 145;
...@@ -140,7 +128,7 @@ class PivotTable extends Component { ...@@ -140,7 +128,7 @@ class PivotTable extends Component {
}, },
}, },
[COLUMN_SPLIT_SETTING]: { [COLUMN_SPLIT_SETTING]: {
section: null, section: t`Columns`,
widget: "fieldsPartition", widget: "fieldsPartition",
persistDefault: true, persistDefault: true,
getHidden: ([{ data }]) => getHidden: ([{ data }]) =>
...@@ -192,6 +180,17 @@ class PivotTable extends Component { ...@@ -192,6 +180,17 @@ class PivotTable extends Component {
return addMissingCardBreakouts(setting, card); return addMissingCardBreakouts(setting, card);
}, },
}, },
"table.column_formatting": {
section: t`Conditional Formatting`,
widget: ChartSettingsTableFormatting,
default: [],
getProps: series => ({
canHighlightRow: false,
cols: series[0].data.cols.filter(isFormattablePivotColumn),
}),
getHidden: ([{ data }]) =>
!data?.cols.some(col => isFormattablePivotColumn(col)),
},
}; };
static columnSettings = { static columnSettings = {
...@@ -257,6 +256,7 @@ class PivotTable extends Component { ...@@ -257,6 +256,7 @@ class PivotTable extends Component {
hasCustomColors, hasCustomColors,
onUpdateVisualizationSettings, onUpdateVisualizationSettings,
isNightMode, isNightMode,
isDashboard,
} = this.props; } = this.props;
if (data == null || !data.cols.some(isPivotGroupColumn)) { if (data == null || !data.cols.some(isPivotGroupColumn)) {
return null; return null;
...@@ -300,49 +300,35 @@ class PivotTable extends Component { ...@@ -300,49 +300,35 @@ class PivotTable extends Component {
} = pivoted; } = pivoted;
const leftHeaderCellRenderer = ({ index, key, style }) => { const leftHeaderCellRenderer = ({ index, key, style }) => {
const { const { value, isSubtotal, hasSubtotal, depth, path, clicked } =
value, leftHeaderItems[index];
isSubtotal,
isGrandTotal,
hasChildren,
hasSubtotal,
depth,
path,
clicked,
} = leftHeaderItems[index];
return ( return (
<div <Cell
key={key} key={key}
style={{ style={{
...style, ...style,
backgroundColor: getBgLightColor(hasCustomColors, isNightMode), ...(depth === 0 ? { paddingLeft: LEFT_HEADER_LEFT_SPACING } : {}),
}} }}
className={cx("overflow-hidden", { isNightMode={isNightMode}
"border-right border-medium": !hasChildren, value={value}
})} isEmphasized={isSubtotal}
> isBold={isSubtotal}
<Cell onClick={this.getCellClickHander(clicked)}
style={depth === 0 ? { paddingLeft: LEFT_HEADER_LEFT_SPACING } : {}} icon={
value={value} (isSubtotal || hasSubtotal) && (
isSubtotal={isSubtotal} <RowToggleIcon
isGrandTotal={isGrandTotal} value={path}
hasCustomColors={hasCustomColors} settings={settings}
onClick={this.getCellClickHander(clicked)} updateSettings={onUpdateVisualizationSettings}
isNightMode={isNightMode} hideUnlessCollapsed={isSubtotal}
icon={ rowIndex={rowIndex} // used to get a list of "other" paths when open one item in a collapsed column
(isSubtotal || hasSubtotal) && ( isNightMode={isNightMode}
<RowToggleIcon />
value={path} )
settings={settings} }
updateSettings={onUpdateVisualizationSettings} />
hideUnlessCollapsed={isSubtotal} // </div>
rowIndex={rowIndex} // used to get a list of "other" paths when open one item in a collapsed column
isNightMode={isNightMode}
/>
)
}
/>
</div>
); );
}; };
const leftHeaderCellSizeAndPositionGetter = ({ index }) => { const leftHeaderCellSizeAndPositionGetter = ({ index }) => {
...@@ -364,25 +350,21 @@ class PivotTable extends Component { ...@@ -364,25 +350,21 @@ class PivotTable extends Component {
const topHeaderHeight = topHeaderRows * CELL_HEIGHT; const topHeaderHeight = topHeaderRows * CELL_HEIGHT;
const topHeaderCellRenderer = ({ index, key, style }) => { const topHeaderCellRenderer = ({ index, key, style }) => {
const { value, hasChildren, clicked } = topHeaderItems[index]; const { value, hasChildren, clicked, isSubtotal, maxDepthBelow } =
topHeaderItems[index];
return ( return (
<div <Cell
key={key} key={key}
style={style} style={{
className={cx("px1 flex align-center cursor-pointer", { ...style,
"border-bottom border-medium": !hasChildren, }}
})} value={value}
isNightMode={isNightMode}
isBorderedHeader={maxDepthBelow === 0}
isEmphasized={hasChildren}
isBold={isSubtotal}
onClick={this.getCellClickHander(clicked)} onClick={this.getCellClickHander(clicked)}
> />
<div
className={cx("flex flex-full full-height align-center", {
"border-bottom": hasChildren,
})}
style={{ width: "100%" }}
>
<Ellipsified>{value}</Ellipsified>
</div>
</div>
); );
}; };
const topHeaderCellSizeAndPositionGetter = ({ index }) => { const topHeaderCellSizeAndPositionGetter = ({ index }) => {
...@@ -405,16 +387,16 @@ class PivotTable extends Component { ...@@ -405,16 +387,16 @@ class PivotTable extends Component {
const bodyRenderer = ({ key, style, rowIndex, columnIndex }) => ( const bodyRenderer = ({ key, style, rowIndex, columnIndex }) => (
<div key={key} style={style} className="flex"> <div key={key} style={style} className="flex">
{getRowSection(columnIndex, rowIndex).map( {getRowSection(columnIndex, rowIndex).map(
({ value, isSubtotal, isGrandTotal, clicked }, index) => ( ({ value, isSubtotal, clicked, backgroundColor }, index) => (
<Cell <Cell
isNightMode={isNightMode}
key={index} key={index}
value={value} value={value}
isSubtotal={isSubtotal} isEmphasized={isSubtotal}
isGrandTotal={isGrandTotal} isBold={isSubtotal}
hasCustomColors={hasCustomColors}
isNightMode={isNightMode}
isBody isBody
onClick={this.getCellClickHander(clicked)} onClick={this.getCellClickHander(clicked)}
backgroundColor={backgroundColor}
/> />
), ),
)} )}
...@@ -422,34 +404,37 @@ class PivotTable extends Component { ...@@ -422,34 +404,37 @@ class PivotTable extends Component {
); );
return ( return (
<div className="no-outline text-small full-height"> <PivotTableRoot isDashboard={isDashboard} isNightMode={isNightMode}>
<ScrollSync> <ScrollSync>
{({ onScroll, scrollLeft, scrollTop }) => ( {({ onScroll, scrollLeft, scrollTop }) => (
<div className="full-height flex flex-column"> <div className="full-height flex flex-column">
<div className="flex" style={{ height: topHeaderHeight }}> <div className="flex" style={{ height: topHeaderHeight }}>
{/* top left corner - displays left header columns */} {/* top left corner - displays left header columns */}
<div <PivotTableTopLeftCellsContainer
className={cx("flex align-end", { isNightMode={isNightMode}
"border-right border-bottom border-medium": leftHeaderWidth,
})}
style={{ style={{
backgroundColor: getBgLightColor(
hasCustomColors,
isNightMode,
),
// add left spacing unless the header width is 0
paddingLeft: leftHeaderWidth && LEFT_HEADER_LEFT_SPACING,
width: leftHeaderWidth, width: leftHeaderWidth,
height: topHeaderHeight,
}} }}
> >
{rowIndexes.map((rowIndex, index) => ( {rowIndexes.map((rowIndex, index) => (
<Cell <Cell
key={rowIndex} key={rowIndex}
value={this.getColumnTitle(rowIndex)} isEmphasized
style={{ width: LEFT_HEADER_CELL_WIDTH }} isBold
hasCustomColors={hasCustomColors} isBorderedHeader
isTransparent
hasTopBorder={topHeaderRows > 1}
isNightMode={isNightMode} isNightMode={isNightMode}
value={this.getColumnTitle(rowIndex)}
style={{
width: LEFT_HEADER_CELL_WIDTH,
...(index === 0
? { paddingLeft: LEFT_HEADER_LEFT_SPACING }
: {}),
...(index === rowIndexes.length - 1
? { borderRight: "none" }
: {}),
}}
icon={ icon={
// you can only collapse before the last column // you can only collapse before the last column
index < rowIndexes.length - 1 && index < rowIndexes.length - 1 &&
...@@ -465,11 +450,12 @@ class PivotTable extends Component { ...@@ -465,11 +450,12 @@ class PivotTable extends Component {
} }
/> />
))} ))}
</div> </PivotTableTopLeftCellsContainer>
{/* top header */} {/* top header */}
<Collection <Collection
ref={e => (this.topHeaderRef = e)} ref={e => (this.topHeaderRef = e)}
className="scroll-hide-all text-medium" className="scroll-hide-all"
isNightMode={isNightMode}
width={width - leftHeaderWidth} width={width - leftHeaderWidth}
height={topHeaderHeight} height={topHeaderHeight}
cellCount={topHeaderItems.length} cellCount={topHeaderItems.length}
...@@ -527,7 +513,7 @@ class PivotTable extends Component { ...@@ -527,7 +513,7 @@ class PivotTable extends Component {
</div> </div>
)} )}
</ScrollSync> </ScrollSync>
</div> </PivotTableRoot>
); );
} }
...@@ -605,13 +591,6 @@ function RowToggleIcon({ ...@@ -605,13 +591,6 @@ function RowToggleIcon({
return ( return (
<RowToggleIconRoot <RowToggleIconRoot
style={{
padding: "4px",
borderRadius: "4px",
backgroundColor: isCollapsed
? getBgLightColor(hasCustomColors, isNightMode)
: getBgDarkColor(hasCustomColors, isNightMode),
}}
onClick={e => { onClick={e => {
e.stopPropagation(); e.stopPropagation();
updateSettings({ updateSettings({
...@@ -626,43 +605,41 @@ function RowToggleIcon({ ...@@ -626,43 +605,41 @@ function RowToggleIcon({
function Cell({ function Cell({
value, value,
isSubtotal,
isGrandTotal,
onClick,
style, style,
isBody = false,
className,
icon, icon,
hasCustomColors, backgroundColor,
isBody = false,
isBold,
isEmphasized,
isNightMode, isNightMode,
isBorderedHeader,
isTransparent,
hasTopBorder,
onClick,
}) { }) {
return ( return (
<div <PivotTableCell
isNightMode={isNightMode}
isBold={isBold}
isEmphasized={isEmphasized}
isBorderedHeader={isBorderedHeader}
hasTopBorder={hasTopBorder}
isTransparent={isTransparent}
style={{ style={{
lineHeight: `${CELL_HEIGHT}px`,
...(isGrandTotal ? { borderTop: "1px solid white" } : {}),
...style, ...style,
...(isSubtotal ...(backgroundColor
? { ? {
backgroundColor: getBgDarkColor(hasCustomColors, isNightMode), backgroundColor,
} }
: {}), : {}),
}} }}
className={cx(
"shrink-below-content-size flex-full flex-basis-none TableInteractive-cellWrapper",
className,
{
"text-bold": isSubtotal,
"cursor-pointer": onClick,
},
)}
onClick={onClick} onClick={onClick}
> >
<div className={cx("px1 flex align-center", { "justify-end": isBody })}> <div className={cx("px1 flex align-center", { "justify-end": isBody })}>
<Ellipsified>{value}</Ellipsified> <Ellipsified>{value}</Ellipsified>
{icon && <div className="pl1">{icon}</div>} {icon && <div className="pl1">{icon}</div>}
</div> </div>
</div> </PivotTableCell>
); );
} }
...@@ -718,3 +695,7 @@ function isColumnValid(col) { ...@@ -718,3 +695,7 @@ function isColumnValid(col) {
isPivotGroupColumn(col) isPivotGroupColumn(col)
); );
} }
function isFormattablePivotColumn(column) {
return column.source === "aggregation";
}
import { css } from "@emotion/react";
import styled from "@emotion/styled"; import styled from "@emotion/styled";
import { color } from "metabase/lib/colors"; import { color, alpha, darken } from "metabase/lib/colors";
export const CELL_HEIGHT = 30;
export const RowToggleIconRoot = styled.div` export const RowToggleIconRoot = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
color: ${color("text-light")}; color: ${color("white")};
padding: 4px;
border-radius: 4px;
background-color: ${color("text-light")};
transition: all 200ms;
outline: none;
&:hover {
background-color: ${darken("text-light", 0.2)};
}
`;
interface PivotTableCellProps {
isBold?: boolean;
isEmphasized?: boolean;
isNightMode?: boolean;
isBorderedHeader?: boolean;
hasTopBorder?: boolean;
isTransparent?: boolean;
}
const getCellBackgroundColor = ({
isEmphasized,
isNightMode,
isTransparent,
}: Partial<PivotTableCellProps>) => {
if (isTransparent) {
return "transparent";
}
if (!isEmphasized) {
return isNightMode ? alpha("bg-black", 0.1) : color("white");
}
return isNightMode ? color("bg-black") : alpha("border", 0.25);
};
const getColor = ({ isNightMode }: PivotTableCellProps) => {
return isNightMode ? color("white") : color("text-dark");
};
const getBorderColor = ({ isNightMode }: PivotTableCellProps) => {
return isNightMode ? alpha("bg-black", 0.8) : color("border");
};
export const PivotTableCell = styled.div<PivotTableCellProps>`
flex: 1 0 auto;
flex-basis: 0;
line-height: ${CELL_HEIGHT}px;
min-width: 0;
min-height: 0;
font-weight: ${props => (props.isBold ? "bold" : "normal")};
cursor: ${props => (props.onClick ? "pointer" : "default")};
color: ${getColor};
box-shadow: -1px 0 0 0 ${getBorderColor} inset;
border-bottom: 1px solid
${props =>
props.isBorderedHeader ? color("bg-dark") : getBorderColor(props)};
background-color: ${getCellBackgroundColor};
${props =>
props.hasTopBorder &&
css`
// compensate the top border
line-height: ${CELL_HEIGHT - 1}px;
border-top: 1px solid ${getBorderColor(props)};
`}
&:hover { &:hover {
color: ${color("brand")}; background-color: ${color("border")};
} }
`; `;
interface PivotTableTopLeftCellsContainerProps {
isNightMode?: boolean;
}
export const PivotTableTopLeftCellsContainer = styled.div<PivotTableTopLeftCellsContainerProps>`
display: flex;
align-items: flex-end;
box-shadow: -1px 0 0 0 ${getBorderColor} inset;
background-color: ${props =>
getCellBackgroundColor({
isEmphasized: true,
isNightMode: props.isNightMode,
})};
`;
interface PivotTableRootProps {
isDashboard?: boolean;
isNightMode?: boolean;
}
export const PivotTableRoot = styled.div<PivotTableRootProps>`
height: 100%;
font-size: 0.875em;
${props =>
props.isDashboard
? css`
border-top: 1px solid ${getBorderColor(props)};
`
: null}
`;
...@@ -294,12 +294,12 @@ describe("scenarios > visualizations > pivot tables", () => { ...@@ -294,12 +294,12 @@ describe("scenarios > visualizations > pivot tables", () => {
cy.log("Collapse the options panel"); cy.log("Collapse the options panel");
cy.icon("chevronup").click(); cy.icon("chevronup").click();
cy.findByText(/Formatting/).should("not.exist"); cy.findByText("Formatting").should("not.exist");
cy.findByText(/See options/).should("not.exist"); cy.findByText(/See options/).should("not.exist");
cy.log("Expand it again"); cy.log("Expand it again");
cy.icon("chevrondown").first().click(); cy.icon("chevrondown").first().click();
cy.findByText(/Formatting/); cy.findByText("Formatting");
cy.findByText(/See options/); cy.findByText(/See options/);
}); });
...@@ -340,7 +340,7 @@ describe("scenarios > visualizations > pivot tables", () => { ...@@ -340,7 +340,7 @@ describe("scenarios > visualizations > pivot tables", () => {
.parent() .parent()
.findAllByText(/Count/) .findAllByText(/Count/)
.click(); .click();
cy.findByText(/Formatting/); cy.findByText("Formatting");
cy.findByText(/See options/).click(); cy.findByText(/See options/).click();
cy.log("New panel for the column options"); cy.log("New panel for the column options");
...@@ -370,7 +370,7 @@ describe("scenarios > visualizations > pivot tables", () => { ...@@ -370,7 +370,7 @@ describe("scenarios > visualizations > pivot tables", () => {
.findAllByText(/Count/) .findAllByText(/Count/)
.click(); .click();
cy.findByText(/Formatting/); cy.findByText("Formatting");
cy.findByText(/Sort order/).should("not.exist"); cy.findByText(/Sort order/).should("not.exist");
}); });
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment