Skip to content
Snippets Groups Projects
Unverified Commit e3f49cdd authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Convert `TagEditorSidebar` to TypeScript (#33227)

* Convert `TagEditorHelp` to TypeScript

* Convert `TagEditorParam` to TypeScript

* Convert `TagEditorSidebar` to TypeScript

* Fix export
parent 0ef26e64
No related merge requests found
......@@ -51,7 +51,7 @@ export function formatParameterValue(
}
// format using the parameter's first targeted field
if (parameter.fields.length > 0) {
if (parameter.fields?.length > 0) {
const [firstField] = parameter.fields;
// when a parameter targets multiple fields we won't know
// which parameter the value is associated with, meaning we
......
......@@ -14,7 +14,7 @@ import Confirm from "metabase/components/Confirm";
import QueryVisualization from "metabase/query_builder/components/QueryVisualization";
import ViewSidebar from "metabase/query_builder/components/view/ViewSidebar";
import DataReference from "metabase/query_builder/components/dataref/DataReference";
import TagEditorSidebar from "metabase/query_builder/components/template_tags/TagEditorSidebar";
import { TagEditorSidebar } from "metabase/query_builder/components/template_tags/TagEditorSidebar";
import { SnippetSidebar } from "metabase/query_builder/components/template_tags/SnippetSidebar/SnippetSidebar";
import { calcInitialEditorHeight } from "metabase/query_builder/components/NativeQueryEditor/utils";
......
/* eslint-disable react/prop-types */
import { t, jt } from "ttag";
import Code from "metabase/components/Code";
import Button from "metabase/core/components/Button";
import ExternalLink from "metabase/core/components/ExternalLink";
import MetabaseSettings from "metabase/lib/settings";
import Utils from "metabase/lib/utils";
import type { DatabaseId, NativeDatasetQuery } from "metabase-types/api";
import type Database from "metabase-lib/metadata/Database";
const SQL_EXAMPLES = {
const SQL_EXAMPLES: Record<string, NativeDatasetQuery> = {
variable: {
database: null,
type: "native",
......@@ -16,7 +17,7 @@ const SQL_EXAMPLES = {
category: {
id: Utils.uuid(),
name: "category",
display_name: "Category",
"display-name": "Category",
type: "text",
required: true,
default: "Widget",
......@@ -33,9 +34,8 @@ const SQL_EXAMPLES = {
created_at: {
id: Utils.uuid(),
name: "created_at",
display_name: "Created At",
"display-name": "Created At",
type: "dimension",
dimension: null,
required: false,
},
},
......@@ -51,7 +51,7 @@ const SQL_EXAMPLES = {
category: {
id: Utils.uuid(),
name: "category",
display_name: "Category",
"display-name": "Category",
type: "text",
required: false,
},
......@@ -68,14 +68,14 @@ const SQL_EXAMPLES = {
id: {
id: Utils.uuid(),
name: "id",
display_name: "ID",
"display-name": "ID",
type: "number",
required: false,
},
category: {
id: Utils.uuid(),
name: "category",
display_name: "Category",
"display-name": "Category",
type: "text",
required: false,
},
......@@ -92,9 +92,8 @@ const SQL_EXAMPLES = {
category: {
id: Utils.uuid(),
name: "category",
display_name: "Category",
"display-name": "Category",
type: "dimension",
dimension: null,
required: false,
},
},
......@@ -102,7 +101,7 @@ const SQL_EXAMPLES = {
},
};
const MONGO_EXAMPLES = {
const MONGO_EXAMPLES: Record<string, NativeDatasetQuery> = {
variable: {
database: null,
type: "native",
......@@ -112,7 +111,7 @@ const MONGO_EXAMPLES = {
category: {
id: Utils.uuid(),
name: "price",
display_name: "Price",
"display-name": "Price",
type: "number",
required: true,
default: "2",
......@@ -129,9 +128,8 @@ const MONGO_EXAMPLES = {
created_at: {
id: Utils.uuid(),
name: "created_at",
display_name: "Created At",
"display-name": "Created At",
type: "dimension",
dimension: null,
required: false,
},
},
......@@ -146,7 +144,7 @@ const MONGO_EXAMPLES = {
category: {
id: Utils.uuid(),
name: "id",
display_name: "ID",
"display-name": "ID",
type: "text",
required: false,
},
......@@ -163,14 +161,14 @@ const MONGO_EXAMPLES = {
id: {
id: Utils.uuid(),
name: "id",
display_name: "ID",
"display-name": "ID",
type: "number",
required: false,
},
category: {
id: Utils.uuid(),
name: "category",
display_name: "Category",
"display-name": "Category",
type: "text",
required: false,
},
......@@ -186,9 +184,8 @@ const MONGO_EXAMPLES = {
category: {
id: Utils.uuid(),
name: "category",
display_name: "Category",
"display-name": "Category",
type: "dimension",
dimension: null,
required: false,
},
},
......@@ -196,7 +193,12 @@ const MONGO_EXAMPLES = {
},
};
const TagExample = ({ datasetQuery, setDatasetQuery }) => (
interface TagExampleProps {
datasetQuery: NativeDatasetQuery;
setDatasetQuery?: (datasetQuery: NativeDatasetQuery, run?: boolean) => void;
}
const TagExample = ({ datasetQuery, setDatasetQuery }: TagExampleProps) => (
<div>
<h5>{t`Example:`}</h5>
<p>
......@@ -215,19 +217,30 @@ const TagExample = ({ datasetQuery, setDatasetQuery }) => (
</div>
);
const TagEditorHelp = ({
interface TagEditorHelpProps {
database?: Database | null;
sampleDatabaseId: DatabaseId;
setDatasetQuery: (datasetQuery: NativeDatasetQuery, run?: boolean) => void;
switchToSettings: () => void;
}
export const TagEditorHelp = ({
database,
setDatasetQuery,
sampleDatabaseId,
setDatasetQuery,
switchToSettings,
}) => {
const driver = database && database.engine;
const examples = driver === "mongo" ? MONGO_EXAMPLES : SQL_EXAMPLES;
const datasetId = driver === "mongo" ? database.id : sampleDatabaseId;
}: TagEditorHelpProps) => {
const engine = database?.engine;
const examples = engine === "mongo" ? MONGO_EXAMPLES : SQL_EXAMPLES;
const datasetId = engine === "mongo" ? database?.id : sampleDatabaseId;
let setQueryWithDatasetId;
let setQueryWithDatasetId = null;
if (datasetId != null) {
setQueryWithDatasetId = (dataset_query, run) => {
setQueryWithDatasetId = (
dataset_query: NativeDatasetQuery,
run?: boolean,
) => {
setDatasetQuery(
{
...dataset_query,
......@@ -306,5 +319,3 @@ const TagEditorHelp = ({
</div>
);
};
export default TagEditorHelp;
import { Component } from "react";
import PropTypes from "prop-types";
import { t } from "ttag";
import _ from "underscore";
import { connect } from "react-redux";
......@@ -8,7 +7,10 @@ import { Link } from "react-router";
import Schemas from "metabase/entities/schemas";
import Toggle from "metabase/core/components/Toggle";
import InputBlurChange from "metabase/components/InputBlurChange";
import Select, { Option } from "metabase/core/components/Select";
import Select, {
Option,
SelectChangeEvent,
} from "metabase/core/components/Select";
import ValuesSourceSettings from "metabase/parameters/components/ValuesSourceSettings";
......@@ -16,13 +18,32 @@ import { fetchField } from "metabase/redux/metadata";
import { getMetadata } from "metabase/selectors/metadata";
import { SchemaTableAndFieldDataSelector } from "metabase/query_builder/components/DataSelector";
import MetabaseSettings from "metabase/lib/settings";
import { canUseCustomSource } from "metabase-lib/parameters/utils/parameter-source";
import type {
DimensionReference,
FieldId,
Parameter,
RowValue,
TemplateTag,
TemplateTagId,
TemplateTagType,
ValuesQueryType,
ValuesSourceConfig,
ValuesSourceType,
} from "metabase-types/api";
import type { State } from "metabase-types/store";
import type Metadata from "metabase-lib/metadata/Metadata";
import type Database from "metabase-lib/metadata/Database";
import type Table from "metabase-lib/metadata/Table";
import type Field from "metabase-lib/metadata/Field";
import { canUseCustomSource } from "metabase-lib/parameters/utils/parameter-source";
import {
getDefaultParameterOptions,
getDefaultParameterWidgetType,
getParameterOptionsForField,
} from "metabase-lib/parameters/utils/template-tag-options";
import {
ContainerLabel,
DefaultParameterValueWidget,
......@@ -32,19 +53,28 @@ import {
TagName,
} from "./TagEditorParam.styled";
const propTypes = {
tag: PropTypes.object.isRequired,
parameter: PropTypes.object,
database: PropTypes.object,
databases: PropTypes.array,
setTemplateTag: PropTypes.func.isRequired,
setTemplateTagConfig: PropTypes.func.isRequired,
setParameterValue: PropTypes.func.isRequired,
fetchField: PropTypes.func.isRequired,
metadata: PropTypes.object.isRequired,
};
export class TagEditorParam extends Component {
interface Props {
tag: TemplateTag;
parameter: Parameter;
database?: Database | null;
databases: Database[];
databaseFields?: Field[];
metadata: Metadata;
setTemplateTag: (tag: TemplateTag) => void;
setTemplateTagConfig: (tag: TemplateTag, config: Parameter) => void;
setParameterValue: (tagId: TemplateTagId, value: RowValue) => void;
fetchField: (fieldId: FieldId, force?: boolean) => void;
}
function mapStateToProps(state: State) {
return {
metadata: getMetadata(state),
};
}
const mapDispatchToProps = { fetchField };
class TagEditorParamInner extends Component<Props> {
UNSAFE_componentWillMount() {
const { tag, fetchField } = this.props;
......@@ -55,7 +85,7 @@ export class TagEditorParam extends Component {
}
}
setType(type) {
setType(type: TemplateTagType) {
const { tag, setTemplateTag } = this.props;
if (tag.type !== type) {
......@@ -69,7 +99,7 @@ export class TagEditorParam extends Component {
}
}
setWidgetType(widgetType) {
setWidgetType(widgetType: string) {
const { tag, setTemplateTag, setParameterValue } = this.props;
if (tag["widget-type"] !== widgetType) {
......@@ -87,7 +117,7 @@ export class TagEditorParam extends Component {
}
}
setRequired(required) {
setRequired(required: boolean) {
const { tag, setTemplateTag } = this.props;
if (tag.required !== required) {
......@@ -95,7 +125,7 @@ export class TagEditorParam extends Component {
}
}
setQueryType = queryType => {
setQueryType = (queryType: ValuesQueryType) => {
const { tag, parameter, setTemplateTagConfig } = this.props;
setTemplateTagConfig(tag, {
......@@ -104,7 +134,10 @@ export class TagEditorParam extends Component {
});
};
setSourceSettings = (sourceType, sourceConfig) => {
setSourceSettings = (
sourceType: ValuesSourceType,
sourceConfig: ValuesSourceConfig,
) => {
const { tag, parameter, setTemplateTagConfig } = this.props;
setTemplateTagConfig(tag, {
......@@ -114,7 +147,7 @@ export class TagEditorParam extends Component {
});
};
setParameterAttribute(attr, val) {
setParameterAttribute(attr: keyof TemplateTag, val: string) {
// only register an update if the value actually changes
if (this.props.tag[attr] !== val) {
this.props.setTemplateTag({
......@@ -124,10 +157,13 @@ export class TagEditorParam extends Component {
}
}
setDimension(fieldId) {
setDimension(fieldId: FieldId) {
const { tag, setTemplateTag, metadata } = this.props;
const dimension = ["field", fieldId, null];
if (!_.isEqual(tag.dimension !== dimension)) {
// TODO Fix raw MBQL usage
const dimension: DimensionReference = ["field", fieldId, null];
if (!_.isEqual(tag.dimension, dimension)) {
const field = metadata.field(dimension[1]);
if (!field) {
return;
......@@ -146,7 +182,7 @@ export class TagEditorParam extends Component {
}
}
getFilterWidgetTypeValue = tag => {
getFilterWidgetTypeValue = (tag: TemplateTag) => {
// avoid `undefined` value because it makes the component "uncontrollable"
// (see Uncontrollable.jsx, metabase#13825)
const widgetType = tag["widget-type"] || "none";
......@@ -161,9 +197,9 @@ export class TagEditorParam extends Component {
render() {
const { tag, database, databases, metadata, parameter } = this.props;
let widgetOptions = [];
let field = null;
let table = null;
let widgetOptions: { name?: string; type: string }[] = [];
let field: Field | null = null;
let table: Table | null | undefined = null;
let fieldMetadataLoaded = false;
if (tag.type === "dimension" && Array.isArray(tag.dimension)) {
field = metadata.field(tag.dimension[1]);
......@@ -177,7 +213,7 @@ export class TagEditorParam extends Component {
const isDimension = tag.type === "dimension";
const hasSelectedDimensionField =
isDimension && Array.isArray(tag.dimension);
const hasWidgetOptions = widgetOptions && widgetOptions.length > 0;
const hasWidgetOptions = widgetOptions?.length > 0;
const hasNoWidgetType =
tag["widget-type"] === "none" || !tag["widget-type"];
const hasNoWidgetLabel = !tag["display-name"];
......@@ -191,7 +227,9 @@ export class TagEditorParam extends Component {
<ContainerLabel>{t`Variable type`}</ContainerLabel>
<Select
value={tag.type}
onChange={e => this.setType(e.target.value)}
onChange={(e: SelectChangeEvent<TemplateTagType>) =>
this.setType(e.target.value)
}
isInitiallyOpen={!tag.type}
placeholder={t`Select…`}
height={300}
......@@ -222,9 +260,11 @@ export class TagEditorParam extends Component {
selectedTableId={table?.id || null}
selectedField={field || null}
selectedFieldId={
hasSelectedDimensionField ? tag.dimension[1] : null
hasSelectedDimensionField ? tag?.dimension?.[1] : null
}
setFieldFn={(fieldId: FieldId) =>
this.setDimension(fieldId)
}
setFieldFn={fieldId => this.setDimension(fieldId)}
className="AdminSelect flex align-center"
isInitiallyOpen={!tag.dimension}
triggerIconSize={12}
......@@ -244,8 +284,10 @@ export class TagEditorParam extends Component {
</ContainerLabel>
<Select
className="block"
value={this.getFilterWidgetTypeValue(tag, widgetOptions)}
onChange={e => this.setWidgetType(e.target.value)}
value={this.getFilterWidgetTypeValue(tag)}
onChange={(e: SelectChangeEvent<string>) =>
this.setWidgetType(e.target.value)
}
isInitiallyOpen={!tag["widget-type"] && hasWidgetOptions}
placeholder={t`Select…`}
>
......@@ -324,9 +366,7 @@ export class TagEditorParam extends Component {
? parameter || {
fields: [],
...tag,
type:
tag["widget-type"] ||
(tag.type === "date" ? "date/single" : null),
type: tag["widget-type"] || null,
}
: {
fields: [],
......@@ -348,8 +388,7 @@ export class TagEditorParam extends Component {
}
}
TagEditorParam.propTypes = propTypes;
export default connect(state => ({ metadata: getMetadata(state) }), {
fetchField,
})(TagEditorParam);
export const TagEditorParam = connect(
mapStateToProps,
mapDispatchToProps,
)(TagEditorParamInner);
import userEvent from "@testing-library/user-event";
import { checkNotNull } from "metabase/core/utils/types";
import { getMetadata } from "metabase/selectors/metadata";
import {
setupDatabasesEndpoints,
......@@ -10,6 +11,7 @@ import { TemplateTag } from "metabase-types/api";
import {
createMockCard,
createMockNativeDatasetQuery,
createMockParameter,
createMockTemplateTag,
} from "metabase-types/api/mocks";
import {
......@@ -21,7 +23,7 @@ import {
createMockQueryBuilderState,
createMockState,
} from "metabase-types/store/mocks";
import TagEditorParam from "./TagEditorParam";
import { TagEditorParam } from "./TagEditorParam";
interface SetupOpts {
tag?: TemplateTag;
......@@ -39,8 +41,11 @@ const setup = ({ tag = createMockTemplateTag() }: SetupOpts = {}) => {
databases: [database],
}),
});
const metadata = getMetadata(state);
const databaseMetadata = checkNotNull(metadata.database(database.id));
setupDatabasesEndpoints([database]);
setupSearchEndpoints([]);
......@@ -51,8 +56,9 @@ const setup = ({ tag = createMockTemplateTag() }: SetupOpts = {}) => {
renderWithProviders(
<TagEditorParam
tag={tag}
database={metadata.database(database.id)}
database={databaseMetadata}
databases={metadata.databasesList()}
parameter={createMockParameter()}
setTemplateTag={setTemplateTag}
setTemplateTagConfig={setTemplateTagConfig}
setParameterValue={setParameterValue}
......
/* eslint-disable react/prop-types */
import { Component } from "react";
import PropTypes from "prop-types";
import { t } from "ttag";
import cx from "classnames";
import _ from "underscore";
import SidebarContent from "metabase/query_builder/components/SidebarContent";
import * as MetabaseAnalytics from "metabase/lib/analytics";
import NativeQuery from "metabase-lib/queries/NativeQuery";
import TagEditorParam from "./TagEditorParam";
import TagEditorHelp from "./TagEditorHelp";
export default class TagEditorSidebar extends Component {
state = {
import type {
Card,
DatabaseId,
NativeDatasetQuery,
Parameter,
ParameterId,
RowValue,
TemplateTag,
TemplateTagId,
} from "metabase-types/api";
import type NativeQuery from "metabase-lib/queries/NativeQuery";
import type Database from "metabase-lib/metadata/Database";
import type Field from "metabase-lib/metadata/Field";
import { TagEditorParam } from "./TagEditorParam";
import { TagEditorHelp } from "./TagEditorHelp";
interface TagEditorSidebarProps {
card: Card;
query: NativeQuery;
databases: Database[];
databaseFields: Field[];
sampleDatabaseId: DatabaseId;
setDatasetQuery: (query: NativeDatasetQuery) => void;
setTemplateTag: (tag: TemplateTag) => void;
setTemplateTagConfig: (tag: TemplateTag, config: Parameter) => void;
setParameterValue: (tagId: TemplateTagId, value: RowValue) => void;
onClose: () => void;
}
interface TagEditorSidebarState {
section: "settings" | "help";
}
export class TagEditorSidebar extends Component<TagEditorSidebarProps> {
state: TagEditorSidebarState = {
section: "settings",
};
......@@ -28,8 +57,8 @@ export default class TagEditorSidebar extends Component {
setParameterValue: PropTypes.func.isRequired,
};
setSection(section) {
this.setState({ section: section });
setSection(section: "settings" | "help") {
this.setState({ section });
MetabaseAnalytics.trackStructEvent(
"QueryBuilder",
"Template Tag Editor Section Change",
......@@ -104,6 +133,17 @@ export default class TagEditorSidebar extends Component {
}
}
interface SettingsPaneProps {
tags: TemplateTag[];
database?: Database | null;
databases: Database[];
databaseFields: Field[];
parametersById: Record<ParameterId, Parameter>;
setTemplateTag: (tag: TemplateTag) => void;
setTemplateTagConfig: (tag: TemplateTag, config: Parameter) => void;
setParameterValue: (tagId: TemplateTagId, value: RowValue) => void;
}
const SettingsPane = ({
tags,
parametersById,
......@@ -113,13 +153,13 @@ const SettingsPane = ({
setTemplateTag,
setTemplateTagConfig,
setParameterValue,
}) => (
}: SettingsPaneProps) => (
<div>
{tags.map(tag => (
<div key={tags.name}>
<div key={tag.name}>
<TagEditorParam
tag={tag}
key={tags.name}
key={tag.name}
parameter={parametersById[tag.id]}
databaseFields={databaseFields}
database={database}
......@@ -132,12 +172,3 @@ const SettingsPane = ({
))}
</div>
);
SettingsPane.propTypes = {
tags: PropTypes.array.isRequired,
query: NativeQuery,
databaseFields: PropTypes.array,
setDatasetQuery: PropTypes.func.isRequired,
setTemplateTag: PropTypes.func.isRequired,
setParameterValue: PropTypes.func.isRequired,
};
......@@ -21,7 +21,7 @@ import DatasetEditor from "../DatasetEditor";
import NativeQueryEditor from "../NativeQueryEditor";
import QueryVisualization from "../QueryVisualization";
import DataReference from "../dataref/DataReference";
import TagEditorSidebar from "../template_tags/TagEditorSidebar";
import { TagEditorSidebar } from "../template_tags/TagEditorSidebar";
import { SnippetSidebar } from "../template_tags/SnippetSidebar";
import SavedQuestionIntroModal from "../SavedQuestionIntroModal";
import QueryModals from "../QueryModals";
......
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