diff --git a/frontend/src/metabase/components/ButtonGroup.jsx b/frontend/src/metabase/components/ButtonGroup.jsx index 7083eeaad179b0ef893c4e865e48492b8342809d..286af5c1a4c193874566f12842a180ce93c4cb02 100644 --- a/frontend/src/metabase/components/ButtonGroup.jsx +++ b/frontend/src/metabase/components/ButtonGroup.jsx @@ -4,6 +4,19 @@ import React from "react"; import cx from "classnames"; +type Value = any; +type Option = any; + +type Props = { + value: Value, + onChange: (value: Value) => void, + options: Option[], + optionNameFn?: (o: Option) => string | React$Element<any>, + optionValueFn?: (o: Option) => Value, + optionKeyFn?: (o: Option) => string, + className?: string, +}; + const ButtonGroup = ({ value, onChange, @@ -12,7 +25,7 @@ const ButtonGroup = ({ optionValueFn = o => o.value, optionKeyFn = optionValueFn, className, -}) => { +}: Props) => { return ( <div className={cx(className, "rounded bordered flex")}> {options.map((o, index) => ( diff --git a/frontend/src/metabase/lib/formatting.js b/frontend/src/metabase/lib/formatting.js index c97dcfd01ca7f9413cf8d63c5c844c08984c9201..f1f2ccdfe9501b78cdf54479708adbf924faf77f 100644 --- a/frontend/src/metabase/lib/formatting.js +++ b/frontend/src/metabase/lib/formatting.js @@ -38,17 +38,19 @@ import type { DatetimeUnit } from "metabase/meta/types/Query"; import type { Moment } from "metabase/meta/types"; export type FormattingOptions = { + // GENERIC column?: Column | Field, majorWidth?: number, type?: "axis" | "cell" | "tooltip", jsx?: boolean, // render links for type/URLs, type/Email, etc rich?: boolean, - // number options: compact?: boolean, // always format as the start value rather than the range, e.x. for bar histogram noRange?: boolean, + // NUMBER // TODO: docoument these: + number_style?: null | "decimal" | "percent" | "scientific" | "currency", prefix?: string, suffix?: string, scale?: number, @@ -61,8 +63,21 @@ export type FormattingOptions = { useGrouping?: boolean, // decimals sets both minimumFractionDigits and maximumFractionDigits decimals?: number, + // STRING + view_as?: "link" | "email_link" | "image", + link_text?: string, + // DATE/TIME + // date/timeout style string that is used to derive a date_format or time_format for different units, see metabase/lib/formatting/date + date_style?: string, + time_style?: string, + date_format?: string, + date_abbreviate?: boolean, + time_format?: string, + time_enabled?: null | "minutes" | "seconds" | "milliseconds", }; +type FormattedString = string | React$Element<any>; + const DEFAULT_NUMBER_OPTIONS: FormattingOptions = { compact: false, maximumFractionDigits: 2, @@ -105,6 +120,7 @@ const RANGE_SEPARATOR = ` – `; export function numberFormatterForOptions(options: FormattingOptions) { options = { ...getDefaultNumberOptions(options), ...options }; // if we don't provide a locale much of the formatting doens't work + // $FlowFixMe: doesn't know about Intl.NumberFormat return new Intl.NumberFormat(options.locale || "en", { style: options.number_style, currency: options.currency, @@ -146,7 +162,10 @@ export function formatNumber(number: number, options: FormattingOptions = {}) { } } -function formatNumberScientific(value: number, options: FormattingOptions) { +function formatNumberScientific( + value: number, + options: FormattingOptions, +): FormattedString { if (options.maximumFractionDigits) { value = d3.round(value, options.maximumFractionDigits); } @@ -207,12 +226,19 @@ export function formatCoordinate( export function formatRange( range: [number, number], - formatter: (value: number) => string, + formatter: (value: number) => any, options: FormattingOptions = {}, ) { - return range - .map(value => formatter(value, options)) - .join(` ${RANGE_SEPARATOR} `); + const [start, end] = range.map(value => formatter(value, options)); + if ((options.jsx && typeof start !== "string") || typeof end !== "string") { + return ( + <span> + {start} {RANGE_SEPARATOR} {end} + </span> + ); + } else { + return `${start} ${RANGE_SEPARATOR} ${start}`; + } } function formatMajorMinor(major, minor, options = {}) { @@ -327,7 +353,7 @@ function formatDateTime(value, options) { options, ); } else { - if (options.show_time === false) { + if (options.time_enabled === false) { return m.format(options.date_abbreviate ? "ll" : "LL"); } else { return m.format(options.date_abbreviate ? "llll" : "LLLL"); @@ -486,6 +512,7 @@ export function formatValue(value: Value, options: FormattingOptions = {}) { </span> ); } else { + // $FlowFixMe: doesn't understand formatted is a string return `${options.prefix || ""}${formatted}${options.suffix || ""}`; } } else { diff --git a/frontend/src/metabase/visualizations/components/TableInteractive.jsx b/frontend/src/metabase/visualizations/components/TableInteractive.jsx index c8025df11fb752b4e623d5c325a25bbc5162dba9..468a1bd9202e4938ce9c1766d5fc84abb9df0fed 100644 --- a/frontend/src/metabase/visualizations/components/TableInteractive.jsx +++ b/frontend/src/metabase/visualizations/components/TableInteractive.jsx @@ -160,7 +160,7 @@ export default class TableInteractive extends Component { } } - _getColumnSettings(props) { + _getColumnSettings(props: Props) { return props.data && props.data.cols.map(col => props.settings.column(col)); } diff --git a/frontend/src/metabase/visualizations/lib/settings.js b/frontend/src/metabase/visualizations/lib/settings.js index 605de962f63d953b864a238a54a5b9f7a409541e..034c16692d47e71b523d30bd5dc3aa957ba667f2 100644 --- a/frontend/src/metabase/visualizations/lib/settings.js +++ b/frontend/src/metabase/visualizations/lib/settings.js @@ -208,7 +208,6 @@ export function getSettingsWidgets( ) { return Object.keys(settingDefs) .map(settingId => - // $FlowFixMe: doesn't understand settingDef is a SettingDef getSettingWidget( settingDefs, settingId, diff --git a/frontend/src/metabase/visualizations/visualizations/Scalar.jsx b/frontend/src/metabase/visualizations/visualizations/Scalar.jsx index a1d0c03c45574d2d0ea0935cc59a3850e8654f19..7e0ed33454a3ee4f87e6d8bbdfd5cc72a8eae54d 100644 --- a/frontend/src/metabase/visualizations/visualizations/Scalar.jsx +++ b/frontend/src/metabase/visualizations/visualizations/Scalar.jsx @@ -17,6 +17,8 @@ import cx from "classnames"; import _ from "underscore"; import type { VisualizationProps } from "metabase/meta/types/Visualization"; +import type { Column } from "metabase/meta/types/Dataset"; +import type { VisualizationSettings } from "metabase/meta/types/Card"; // convert legacy `scalar.*` visualization settings to format options function legacyScalarSettingsToFormatOptions(settings) { @@ -126,7 +128,7 @@ export default class Scalar extends Component { }, }; - _getColumnIndex(cols, settings) { + _getColumnIndex(cols: Column[], settings: VisualizationSettings) { const columnIndex = _.findIndex( cols, col => col.name === settings["scalar.field"], diff --git a/frontend/src/metabase/visualizations/visualizations/Table.jsx b/frontend/src/metabase/visualizations/visualizations/Table.jsx index cd33046668024e7c915b8ac3d97b1edd1f93c05d..dc4869f6078bb51f8686459164ad10b1a2abb779 100644 --- a/frontend/src/metabase/visualizations/visualizations/Table.jsx +++ b/frontend/src/metabase/visualizations/visualizations/Table.jsx @@ -130,7 +130,9 @@ export default class Table extends Component { } if (isString(column)) { let defaultValue = null; - const options = [{ name: t`Off`, value: null }]; + const options: { name: string, value: null | string }[] = [ + { name: t`Off`, value: null }, + ]; if (!column.special_type || isURL(column)) { defaultValue = "link"; options.push({ name: t`Link`, value: "link" });