diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx index 3a808290638828c5f7026bc07d97373a196fcffa..a3aa5a5fc12e3ddc8d309e7f80ea4dd9faae5b1a 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx @@ -14,7 +14,6 @@ import NumericInput from "metabase/components/NumericInput"; import { SortableContainer, SortableElement } from "react-sortable-hoc"; import MetabaseAnalytics from "metabase/lib/analytics"; -import { capitalize } from "metabase/lib/formatting"; import { isNumeric, isString } from "metabase/lib/schema_metadata"; import _ from "underscore"; @@ -22,21 +21,25 @@ import d3 from "d3"; import cx from "classnames"; const NUMBER_OPERATOR_NAMES = { - "<": t`less than`, - ">": t`greater than`, - "<=": t`less than or equal to`, - ">=": t`greater than or equal to`, - "=": t`equal to`, - "!=": t`not equal to`, - "is-null": t`null`, - "not-null": t`not null`, + "<": t`is less than`, + ">": t`is greater than`, + "<=": t`is less than or equal to`, + ">=": t`is greater than or equal to`, + "=": t`is equal to`, + "!=": t`is not equal to`, + "is-null": t`is null`, + "not-null": t`is not null`, }; const STRING_OPERATOR_NAMES = { - "=": t`equal to`, - "!=": t`not equal to`, - "is-null": t`null`, - "not-null": t`not null`, + "=": t`is equal to`, + "!=": t`is not equal to`, + "is-null": t`is null`, + "not-null": t`is not null`, + contains: t`contains`, + "does-not-contain": t`does not contain`, + "starts-with": t`starts with`, + "ends-with": t`ends with`, }; const ALL_OPERATOR_NAMES = { @@ -293,7 +296,7 @@ const RuleDescription = ({ rule }) => ( {rule.type === "range" ? t`Cells in this column will be tinted based on their values.` : rule.type === "single" - ? jt`When a cell in these columns is ${( + ? jt`When a cell in these columns ${( <span className="text-bold"> {ALL_OPERATOR_NAMES[rule.operator]} {rule.value} </span> @@ -352,7 +355,7 @@ const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => { )} {rule.type === "single" ? ( <div> - <h3 className="mt3 mb1">{t`When a cell in this column is…`}</h3> + <h3 className="mt3 mb1">{t`When a cell in this column…`}</h3> <Select value={rule.operator} onChange={e => onChange({ ...rule, operator: e.target.value })} @@ -360,7 +363,7 @@ const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => { {Object.entries( isNumericRule ? NUMBER_OPERATOR_NAMES : STRING_OPERATOR_NAMES, ).map(([operator, operatorName]) => ( - <Option value={operator}>{capitalize(operatorName)}</Option> + <Option value={operator}>{operatorName}</Option> ))} </Select> {hasOperand && isNumericRule ? ( diff --git a/frontend/src/metabase/visualizations/lib/table_format.js b/frontend/src/metabase/visualizations/lib/table_format.js index 6ded6e5b80c0df685afd5113b37e9fcd11d7a4cc..94a073e733a5bb809b4aca1fc0d55c413131d11f 100644 --- a/frontend/src/metabase/visualizations/lib/table_format.js +++ b/frontend/src/metabase/visualizations/lib/table_format.js @@ -21,7 +21,19 @@ type SingleFormat = { type: "single", columns: ColumnName[], color: Color, - operator: "<" | ">" | "<=" | ">=" | "=" | "!=" | "is-null" | "not-null", + operator: + | "<" + | ">" + | "<=" + | ">=" + | "=" + | "!=" + | "is-null" + | "not-null" + | "contains" + | "does-not-contain" + | "starts-with" + | "ends-with", value: number | string, highlight_row: boolean, }; @@ -142,6 +154,34 @@ function compileFormatter( return v => (v === null ? color : null); case "not-null": return v => (v !== null ? color : null); + case "contains": + return v => + typeof value === "string" && + typeof v === "string" && + v.indexOf(value) >= 0 + ? color + : null; + case "does-not-contain": + return v => + typeof value === "string" && + typeof v === "string" && + v.indexOf(value) < 0 + ? color + : null; + case "starts-with": + return v => + typeof value === "string" && + typeof v === "string" && + v.startsWith(value) + ? color + : null; + case "ends-with": + return v => + typeof value === "string" && + typeof v === "string" && + v.endsWith(value) + ? color + : null; } } else if (format.type === "range") { const columnMin = name =>