diff --git a/superset-frontend/src/explore/components/ControlHeader.jsx b/superset-frontend/src/explore/components/ControlHeader.jsx
deleted file mode 100644
index b78733135db25..0000000000000
--- a/superset-frontend/src/explore/components/ControlHeader.jsx
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import { t, css, withTheme } from '@superset-ui/core';
-import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
-import { Tooltip } from 'src/components/Tooltip';
-import { FormLabel } from 'src/components/Form';
-import Icons from 'src/components/Icons';
-
-const propTypes = {
- name: PropTypes.string,
- label: PropTypes.node,
- description: PropTypes.node,
- validationErrors: PropTypes.array,
- renderTrigger: PropTypes.bool,
- rightNode: PropTypes.node,
- leftNode: PropTypes.node,
- onClick: PropTypes.func,
- hovered: PropTypes.bool,
- tooltipOnClick: PropTypes.func,
- warning: PropTypes.string,
- danger: PropTypes.string,
-};
-
-const defaultProps = {
- validationErrors: [],
- renderTrigger: false,
- hovered: false,
- name: undefined,
-};
-
-class ControlHeader extends React.Component {
- renderOptionalIcons() {
- if (this.props.hovered) {
- return (
- css`
- position: absolute;
- top: 50%;
- right: 0;
- padding-left: ${theme.gridUnit}px;
- transform: translate(100%, -50%);
- white-space: nowrap;
- `}
- >
- {this.props.description && (
-
- {' '}
-
- )}
- {this.props.renderTrigger && (
-
- {' '}
-
- )}
-
- );
- }
- return null;
- }
-
- render() {
- if (!this.props.label) {
- return null;
- }
- const labelClass =
- this.props.validationErrors.length > 0 ? 'text-danger' : '';
-
- const { theme } = this.props;
-
- return (
-
-
-
- {this.props.leftNode && {this.props.leftNode}}
-
- {this.props.label}
- {' '}
- {this.props.warning && (
-
-
-
- {' '}
-
- )}
- {this.props.danger && (
-
-
-
- {' '}
-
- )}
- {this.props.validationErrors.length > 0 && (
-
-
-
- {' '}
-
- )}
- {this.renderOptionalIcons()}
-
-
- {this.props.rightNode && (
-
{this.props.rightNode}
- )}
-
-
- );
- }
-}
-
-ControlHeader.propTypes = propTypes;
-ControlHeader.defaultProps = defaultProps;
-
-export default withTheme(ControlHeader);
diff --git a/superset-frontend/src/explore/components/ControlHeader.stories.tsx b/superset-frontend/src/explore/components/ControlHeader.stories.tsx
new file mode 100644
index 0000000000000..af9df9dece8d3
--- /dev/null
+++ b/superset-frontend/src/explore/components/ControlHeader.stories.tsx
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import ControlHeader, { ControlHeaderProps } from './ControlHeader';
+
+export default {
+ title: 'ControlHeader',
+ component: ControlHeader,
+};
+
+const options: {
+ [key: string]: ControlHeaderProps;
+} = {
+ label: {
+ label: 'Control label',
+ },
+ warning: {
+ label: 'Control warning',
+ warning: 'Example of warning message',
+ },
+ error: {
+ label: 'Control error',
+ validationErrors: ['Something is wrong'],
+ },
+};
+
+export const ControlHeaderGallery = () => (
+ <>
+ {Object.entries(options).map(([name, props]) => (
+ <>
+ {name}
+
+ >
+ ))}
+ >
+);
+
+export const InteractiveControlHeader = (props: ControlHeaderProps) => (
+
+);
+
+InteractiveControlHeader.args = {
+ label: 'example label',
+ description: 'example description',
+ warning: 'example warning',
+ renderTrigger: false,
+ hovered: false,
+};
+
+InteractiveControlHeader.argTypes = {
+ tooltipOnClick: { action: 'tooltipOnClick' },
+ onClick: { action: 'onClick' },
+};
+
+InteractiveControlHeader.story = {
+ parameters: {
+ knobs: {
+ disable: true,
+ },
+ },
+};
diff --git a/superset-frontend/src/explore/components/ControlHeader.tsx b/superset-frontend/src/explore/components/ControlHeader.tsx
new file mode 100644
index 0000000000000..ce240704b5d3f
--- /dev/null
+++ b/superset-frontend/src/explore/components/ControlHeader.tsx
@@ -0,0 +1,158 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React, { FC, ReactNode } from 'react';
+import { t, css, useTheme } from '@superset-ui/core';
+import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
+import { Tooltip } from 'src/components/Tooltip';
+import { FormLabel } from 'src/components/Form';
+import Icons from 'src/components/Icons';
+
+type ValidationError = string;
+
+export type ControlHeaderProps = {
+ name?: string;
+ label?: ReactNode;
+ description?: ReactNode;
+ validationErrors?: ValidationError[];
+ renderTrigger?: boolean;
+ rightNode?: ReactNode;
+ leftNode?: ReactNode;
+ onClick?: () => void;
+ hovered?: boolean;
+ tooltipOnClick?: () => void;
+ warning?: string;
+ danger?: string;
+};
+
+const ControlHeader: FC = ({
+ name,
+ label,
+ description,
+ validationErrors = [],
+ renderTrigger = false,
+ rightNode,
+ leftNode,
+ onClick,
+ hovered = false,
+ tooltipOnClick = () => {},
+ warning,
+ danger,
+}) => {
+ const { gridUnit, colors } = useTheme();
+
+ if (!label) {
+ return null;
+ }
+
+ const renderOptionalIcons = () => {
+ if (!hovered) {
+ return null;
+ }
+
+ return (
+ css`
+ position: absolute;
+ top: 50%;
+ right: 0;
+ padding-left: ${gridUnit}px;
+ transform: translate(100%, -50%);
+ white-space: nowrap;
+ `}
+ >
+ {description && (
+
+ {' '}
+
+ )}
+ {renderTrigger && (
+
+ {' '}
+
+ )}
+
+ );
+ };
+
+ const labelClass = validationErrors?.length > 0 ? 'text-danger' : '';
+
+ return (
+
+
+
+ {leftNode && {leftNode}}
+
+ {label}
+ {' '}
+ {warning && (
+
+
+
+ {' '}
+
+ )}
+ {danger && (
+
+
+
+ {' '}
+
+ )}
+ {validationErrors?.length > 0 && (
+
+
+
+ {' '}
+
+ )}
+ {renderOptionalIcons()}
+
+
+ {rightNode &&
{rightNode}
}
+
+
+ );
+};
+
+export default ControlHeader;