diff --git a/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx b/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx index f2cf04d98736f1fd8322998c779f98a1185c6e18..339047e4066599124752721b73e13190200b4b27 100644 --- a/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx +++ b/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx @@ -13,6 +13,7 @@ import DatabaseEditForms from "../components/DatabaseEditForms.jsx"; import DatabaseSchedulingForm from "../components/DatabaseSchedulingForm"; import ActionButton from "metabase/components/ActionButton.jsx"; import Breadcrumbs from "metabase/components/Breadcrumbs.jsx"; +import Radio from "metabase/components/Radio.jsx"; import ModalWithTrigger from "metabase/components/ModalWithTrigger.jsx"; import colors from "metabase/lib/colors"; @@ -43,35 +44,6 @@ const mapStateToProps = (state, props) => ({ formState: getFormState(state), }); -export const Tab = ({ name, setTab, currentTab }) => { - const isCurrentTab = currentTab === name.toLowerCase(); - - return ( - <div - className={cx("cursor-pointer py2", { "text-brand": isCurrentTab })} - // TODO Use css classes instead? - style={ - isCurrentTab ? { borderBottom: `3px solid ${colors["brand"]}` } : {} - } - onClick={() => setTab(name)} - > - <h3>{name}</h3> - </div> - ); -}; - -export const Tabs = ({ tabs, currentTab, setTab }) => ( - <div className="border-bottom"> - <ol className="Form-offset flex align center"> - {tabs.map((tab, index) => ( - <li key={index} className="mr3"> - <Tab name={tab} setTab={setTab} currentTab={currentTab} /> - </li> - ))} - </ol> - </div> -); - const mapDispatchToProps = { reset, initializeDatabase, @@ -84,18 +56,26 @@ const mapDispatchToProps = { selectEngine, }; +type TabName = "connection" | "scheduling"; +type TabOption = { name: TabName, value: string }; + +const TABS: TabOption[] = [ + { name: t`Connection`, value: "connection" }, + { name: t`Scheduling`, value: "scheduling" }, +]; + @connect(mapStateToProps, mapDispatchToProps) @title(({ database }) => database && database.name) export default class DatabaseEditApp extends Component { state: { - currentTab: "connection" | "scheduling", + currentTab: TabName, }; constructor(props, context) { super(props, context); this.state = { - currentTab: "connection", + currentTab: TABS[0].value, }; } @@ -156,13 +136,14 @@ export default class DatabaseEditApp extends Component { <div className="Grid-cell"> <div className="Form-new bordered rounded shadowed pt0"> {showTabs && ( - <Tabs - tabs={[t`Connection`, t`Scheduling`]} - currentTab={currentTab} - setTab={tab => - this.setState({ currentTab: tab.toLowerCase() }) - } - /> + <div className="Form-offset border-bottom"> + <Radio + value={currentTab} + options={TABS} + onChange={currentTab => this.setState({ currentTab })} + underlined + /> + </div> )} <LoadingAndErrorWrapper loading={!database} error={null}> {() => ( diff --git a/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx b/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx index dee048a4d68e9459d5841e528b123aa99164599a..837e2aac14889fe03b730a7888bab952743bed45 100644 --- a/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx +++ b/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx @@ -435,15 +435,19 @@ export default class PeopleListingApp extends Component { let title = t`People`; if (deactivated.length > 0) { title = ( - <Radio - className="h6" - value={!!showDeactivated} - options={[ - { name: t`Active`, value: false }, - { name: t`Deactivated`, value: true }, - ]} - onChange={showDeactivated => this.setState({ showDeactivated })} - /> + <div className="mb2"> + <Radio + className="h6" + value={!!showDeactivated} + options={[ + { name: t`Active`, value: false }, + { name: t`Deactivated`, value: true }, + ]} + underlined + py={1} + onChange={showDeactivated => this.setState({ showDeactivated })} + /> + </div> ); } diff --git a/frontend/src/metabase/components/Radio.info.js b/frontend/src/metabase/components/Radio.info.js index f9d165e45bf86b65274724157fe2c0cc81ed8d71..bbc53a33e911219ccbd87535cb6f0ed5e1fda543 100644 --- a/frontend/src/metabase/components/Radio.info.js +++ b/frontend/src/metabase/components/Radio.info.js @@ -14,6 +14,7 @@ const PROPS = { export const examples = { default: <Radio {...PROPS} />, + underlined: <Radio {...PROPS} underlined />, "show buttons": <Radio {...PROPS} showButtons />, - vertical: <Radio {...PROPS} isVertical />, + vertical: <Radio {...PROPS} vertical />, }; diff --git a/frontend/src/metabase/components/Radio.jsx b/frontend/src/metabase/components/Radio.jsx index 53843c39b7b6f5767e6a2cc91542926cd2d77dc7..d4d42a7202ce61b40869d7a4640680118cd34637 100644 --- a/frontend/src/metabase/components/Radio.jsx +++ b/frontend/src/metabase/components/Radio.jsx @@ -1,6 +1,8 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; +import colors from "metabase/lib/colors"; + import cx from "classnames"; import _ from "underscore"; @@ -12,15 +14,18 @@ export default class Radio extends Component { optionNameFn: PropTypes.func, optionValueFn: PropTypes.func, optionKeyFn: PropTypes.func, - isVertical: PropTypes.bool, + vertical: PropTypes.bool, + underlined: PropTypes.bool, showButtons: PropTypes.bool, + py: PropTypes.number, }; static defaultProps = { optionNameFn: option => option.name, optionValueFn: option => option.value, optionKeyFn: option => option.value, - isVertical: false, + vertical: false, + underlined: false, }; constructor(props, context) { @@ -36,45 +41,56 @@ export default class Radio extends Component { optionNameFn, optionValueFn, optionKeyFn, - isVertical, + vertical, + underlined, className, + py, } = this.props; // show buttons for vertical only by default const showButtons = - this.props.showButtons != undefined ? this.props.showButtons : isVertical; + this.props.showButtons != undefined ? this.props.showButtons : vertical; return ( <ul className={cx(className, "flex", { - "flex-column": isVertical, + "flex-column": vertical, "text-bold h3": !showButtons, })} > - {options.map(option => ( - <li - key={optionKeyFn(option)} - className={cx("flex align-center cursor-pointer mt1 mr2", { - "text-brand-hover": !showButtons, - })} - onClick={e => onChange(optionValueFn(option))} - > - <input - className="Form-radio" - type="radio" - name={this._id} - value={optionValueFn(option)} - checked={value === optionValueFn(option)} - id={this._id + "-" + optionKeyFn(option)} - /> - {showButtons && ( - <label htmlFor={this._id + "-" + optionKeyFn(option)} /> - )} - <span - className={cx({ "text-brand": value === optionValueFn(option) })} + {options.map(option => { + const selected = value === optionValueFn(option); + + return ( + <li + key={optionKeyFn(option)} + className={cx( + "flex align-center cursor-pointer mr2", + { "text-brand-hover": !showButtons }, + py != undefined ? `py${py}` : underlined ? "py2" : "pt1", + )} + style={{ + borderBottom: underlined ? `3px solid transparent` : undefined, + borderColor: + selected && underlined ? colors["brand"] : "transparent", + }} + onClick={e => onChange(optionValueFn(option))} > - {optionNameFn(option)} - </span> - </li> - ))} + <input + className="Form-radio" + type="radio" + name={this._id} + value={optionValueFn(option)} + checked={selected} + id={this._id + "-" + optionKeyFn(option)} + /> + {showButtons && ( + <label htmlFor={this._id + "-" + optionKeyFn(option)} /> + )} + <span className={cx({ "text-brand": selected })}> + {optionNameFn(option)} + </span> + </li> + ); + })} </ul> ); } diff --git a/frontend/src/metabase/containers/SaveQuestionModal.jsx b/frontend/src/metabase/containers/SaveQuestionModal.jsx index 950495ea9ebacfe1cfa102a59b5804105a540b2b..aade70fcee6d146a130f14eaec8f8401b789bc17 100644 --- a/frontend/src/metabase/containers/SaveQuestionModal.jsx +++ b/frontend/src/metabase/containers/SaveQuestionModal.jsx @@ -177,7 +177,7 @@ export default class SaveQuestionModal extends Component { }, { name: t`Save as new question`, value: "create" }, ]} - isVertical + vertical /> </FormField> ); diff --git a/frontend/src/metabase/visualizations/components/ChartSettings.jsx b/frontend/src/metabase/visualizations/components/ChartSettings.jsx index 2311f840c63f5b99193da57b4cecf9ab177097f2..42cfb4a172f102dbbf60b243533d324ad9f7f577 100644 --- a/frontend/src/metabase/visualizations/components/ChartSettings.jsx +++ b/frontend/src/metabase/visualizations/components/ChartSettings.jsx @@ -6,6 +6,7 @@ import { t } from "c-3po"; import Warnings from "metabase/query_builder/components/Warnings.jsx"; import Button from "metabase/components/Button"; +import Radio from "metabase/components/Radio"; import Visualization from "metabase/visualizations/components/Visualization.jsx"; import { getSettingsWidgets } from "metabase/visualizations/lib/settings"; @@ -138,21 +139,14 @@ class ChartSettings extends Component { <div className="flex flex-column spread"> {tabNames.length > 1 && ( <div className="border-bottom flex flex-no-shrink pl4"> - {tabNames.map(tabName => ( - <div - className={cx( - "h3 py2 mr2 border-bottom cursor-pointer text-brand-hover border-brand-hover", - { - "text-brand border-brand": currentTab === tabName, - "border-transparent": currentTab !== tabName, - }, - )} - style={{ borderWidth: 3 }} - onClick={() => this.handleSelectTab(tabName)} - > - {tabName} - </div> - ))} + <Radio + value={currentTab} + onChange={this.handleSelectTab} + options={tabNames} + optionNameFn={v => v} + optionValueFn={v => v} + underlined + /> </div> )} <div className="full-height relative"> diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingRadio.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingRadio.jsx index 57e6a351449705bf5953d354985abaede2b3dfd1..4b0d8c79cb44c5ae649e2ec64dde278ef5326b97 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartSettingRadio.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingRadio.jsx @@ -8,7 +8,7 @@ const ChartSettingRadio = ({ value, onChange, options = [], className }) => ( value={value} onChange={onChange} options={options} - isVertical + vertical /> ); diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx index b541141eb70567489ec242a342aa6cc0a0f53001..b4107be8bbd15ea23d3bbe16ad1c7e0598af6416 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx @@ -305,7 +305,7 @@ const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => ( { name: t`Color range`, value: "range" }, ]} onChange={type => onChange({ ...DEFAULTS_BY_TYPE[type], ...rule, type })} - isVertical + vertical /> {rule.type === "single" ? ( <div> @@ -355,7 +355,7 @@ const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => ( }, ] ).concat([{ name: t`Custom value`, value: "custom" }])} - isVertical + vertical /> {rule.min_type === "custom" && ( <NumericInput @@ -377,7 +377,7 @@ const RuleEditor = ({ rule, cols, isNew, onChange, onDone, onRemove }) => ( }, ] ).concat([{ name: t`Custom value`, value: "custom" }])} - isVertical + vertical /> {rule.max_type === "custom" && ( <NumericInput