diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorScheme.test.jsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorScheme.test.jsx
deleted file mode 100644
index 565c4f9f28b54..0000000000000
--- a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorScheme.test.jsx
+++ /dev/null
@@ -1,43 +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.
- */
-/* eslint-disable no-unused-expressions */
-import React from 'react';
-import { Select } from 'src/components';
-import { getCategoricalSchemeRegistry } from '@superset-ui/core';
-import { styledMount as mount } from 'spec/helpers/theming';
-import ColorSchemeControl from 'src/explore/components/controls/ColorSchemeControl';
-
-const defaultProps = {
- name: 'color_scheme',
- label: 'Color Scheme',
- options: getCategoricalSchemeRegistry()
- .keys()
- .map(s => [s, s]),
-};
-
-describe('ColorSchemeControl', () => {
- let wrapper;
- beforeEach(() => {
- wrapper = mount();
- });
-
- it('renders a Select', () => {
- expect(wrapper.find(Select)).toExist();
- });
-});
diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx
index 1f19d8a2c3a89..9e760aab13166 100644
--- a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx
+++ b/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx
@@ -18,7 +18,7 @@
*/
import React from 'react';
import { render, screen, waitFor } from 'spec/helpers/testing-library';
-import ColorSchemeControl from '.';
+import ColorSchemeControl, { ColorSchemes } from '.';
const defaultProps = {
hasCustomLabelColors: false,
@@ -28,7 +28,7 @@ const defaultProps = {
value: 'supersetDefault',
clearable: true,
choices: [],
- schemes: () => null,
+ schemes: () => ({} as ColorSchemes),
isLinear: false,
};
diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.jsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.jsx
deleted file mode 100644
index d5855207859d1..0000000000000
--- a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.jsx
+++ /dev/null
@@ -1,184 +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 { isFunction } from 'lodash';
-import { Select } from 'src/components';
-import { Tooltip } from 'src/components/Tooltip';
-import { styled, t } from '@superset-ui/core';
-import Icons from 'src/components/Icons';
-import ControlHeader from 'src/explore/components/ControlHeader';
-import ColorSchemeLabel from './ColorSchemeLabel';
-
-const propTypes = {
- hasCustomLabelColors: PropTypes.bool,
- dashboardId: PropTypes.number,
- description: PropTypes.string,
- label: PropTypes.string,
- labelMargin: PropTypes.number,
- name: PropTypes.string.isRequired,
- onChange: PropTypes.func,
- value: PropTypes.string,
- clearable: PropTypes.bool,
- default: PropTypes.string,
- choices: PropTypes.oneOfType([
- PropTypes.arrayOf(PropTypes.array),
- PropTypes.func,
- ]),
- schemes: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
- isLinear: PropTypes.bool,
-};
-
-const defaultProps = {
- choices: [],
- hasCustomLabelColors: false,
- label: t('Color scheme'),
- schemes: {},
- clearable: false,
- onChange: () => {},
-};
-
-const StyledAlert = styled(Icons.AlertSolid)`
- color: ${({ theme }) => theme.colors.alert.base};
-`;
-
-export default class ColorSchemeControl extends React.PureComponent {
- constructor(props) {
- super(props);
- this.onChange = this.onChange.bind(this);
- this.renderOption = this.renderOption.bind(this);
- this.renderLabel = this.renderLabel.bind(this);
- this.dashboardColorSchemeAlert = t(
- `The color scheme is determined by the related dashboard.
- Edit the color scheme in the dashboard properties.`,
- );
- }
-
- onChange(value) {
- this.props.onChange(value);
- }
-
- renderOption(value) {
- const { isLinear } = this.props;
- const currentScheme = this.schemes[value];
-
- // For categorical scheme, display all the colors
- // For sequential scheme, show 10 or interpolate to 10.
- // Sequential schemes usually have at most 10 colors.
- let colors = [];
- if (currentScheme) {
- colors = isLinear ? currentScheme.getColors(10) : currentScheme.colors;
- }
-
- return (
-
- );
- }
-
- renderLabel() {
- const { dashboardId, hasCustomLabelColors, label } = this.props;
-
- if (hasCustomLabelColors || dashboardId) {
- const alertTitle = hasCustomLabelColors
- ? t(
- `This color scheme is being overriden by custom label colors.
- Check the JSON metadata in the Advanced settings`,
- )
- : this.dashboardColorSchemeAlert;
- return (
- <>
- {label}{' '}
-
-
-
- >
- );
- }
- return label;
- }
-
- render() {
- const { choices, dashboardId, schemes } = this.props;
- let options = dashboardId
- ? [
- {
- value: 'dashboard',
- label: 'dashboard',
- customLabel: (
-
- {t('Dashboard scheme')}
-
- ),
- },
- ]
- : [];
- let currentScheme = dashboardId ? 'dashboard' : undefined;
-
- // if related to a dashboard the scheme is dictated by the dashboard
- if (!dashboardId) {
- this.schemes = isFunction(schemes) ? schemes() : schemes;
- const controlChoices = isFunction(choices) ? choices() : choices;
- const allColorOptions = [];
- const filteredColorOptions = controlChoices.filter(o => {
- const option = o[0];
- const isValidColorOption =
- option !== 'SUPERSET_DEFAULT' && !allColorOptions.includes(option);
- allColorOptions.push(option);
- return isValidColorOption;
- });
-
- options = filteredColorOptions.map(([value]) => ({
- customLabel: this.renderOption(value),
- label: this.schemes?.[value]?.label || value,
- value,
- }));
-
- currentScheme = this.props.value || this.props.default;
-
- if (currentScheme === 'SUPERSET_DEFAULT') {
- currentScheme = this.schemes?.SUPERSET_DEFAULT?.id;
- }
- }
-
- const selectProps = {
- ariaLabel: t('Select color scheme'),
- allowClear: this.props.clearable,
- disabled: !!dashboardId,
- name: `select-${this.props.name}`,
- onChange: this.onChange,
- options,
- placeholder: t('Select scheme'),
- value: currentScheme,
- };
-
- return (
- }
- {...selectProps}
- />
- );
- }
-}
-
-ColorSchemeControl.propTypes = propTypes;
-ColorSchemeControl.defaultProps = defaultProps;
diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx
new file mode 100644
index 0000000000000..df98839d2b8a3
--- /dev/null
+++ b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx
@@ -0,0 +1,189 @@
+/**
+ * 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, { useMemo } from 'react';
+import { ColorScheme, SequentialScheme, styled, t } from '@superset-ui/core';
+import { isFunction } from 'lodash';
+import { Select } from 'src/components';
+import ControlHeader from 'src/explore/components/ControlHeader';
+import { Tooltip } from 'src/components/Tooltip';
+import Icons from 'src/components/Icons';
+import ColorSchemeLabel from './ColorSchemeLabel';
+
+export interface ColorSchemes {
+ [key: string]: ColorScheme;
+}
+
+export interface ColorSchemeControlProps {
+ hasCustomLabelColors: boolean;
+ dashboardId?: number;
+ label: string;
+ name: string;
+ onChange?: (value: string) => void;
+ value: string;
+ clearable: boolean;
+ defaultScheme?: string;
+ choices: string[][] | (() => string[][]);
+ schemes: ColorSchemes | (() => ColorSchemes);
+ isLinear: boolean;
+}
+
+const StyledAlert = styled(Icons.AlertSolid)`
+ color: ${({ theme }) => theme.colors.alert.base};
+`;
+
+const CUSTOM_LABEL_ALERT = t(
+ `This color scheme is being overriden by custom label colors.
+ Check the JSON metadata in the Advanced settings`,
+);
+
+const DASHBOARD_ALERT = t(
+ `The color scheme is determined by the related dashboard.
+ Edit the color scheme in the dashboard properties.`,
+);
+
+const Label = ({
+ label,
+ hasCustomLabelColors,
+ dashboardId,
+}: Pick<
+ ColorSchemeControlProps,
+ 'label' | 'hasCustomLabelColors' | 'dashboardId'
+>) => {
+ if (hasCustomLabelColors || dashboardId) {
+ const alertTitle = hasCustomLabelColors
+ ? CUSTOM_LABEL_ALERT
+ : DASHBOARD_ALERT;
+ return (
+ <>
+ {label}{' '}
+
+
+
+ >
+ );
+ }
+ return <>{label}>;
+};
+
+const ColorSchemeControl = ({
+ hasCustomLabelColors = false,
+ dashboardId,
+ label = t('Color scheme'),
+ name,
+ onChange = () => {},
+ value,
+ clearable = false,
+ defaultScheme,
+ choices = [],
+ schemes = {},
+ isLinear,
+ ...rest
+}: ColorSchemeControlProps) => {
+ const currentScheme = useMemo(() => {
+ if (dashboardId) {
+ return 'dashboard';
+ }
+ let result = value || defaultScheme;
+ if (result === 'SUPERSET_DEFAULT') {
+ const schemesObject = isFunction(schemes) ? schemes() : schemes;
+ result = schemesObject?.SUPERSET_DEFAULT?.id;
+ }
+ return result;
+ }, [dashboardId, defaultScheme, schemes, value]);
+
+ const options = useMemo(() => {
+ if (dashboardId) {
+ return [
+ {
+ value: 'dashboard',
+ label: 'dashboard',
+ customLabel: (
+ {t('Dashboard scheme')}
+ ),
+ },
+ ];
+ }
+ const schemesObject = isFunction(schemes) ? schemes() : schemes;
+ const controlChoices = isFunction(choices) ? choices() : choices;
+ const allColorOptions: string[] = [];
+ const filteredColorOptions = controlChoices.filter(o => {
+ const option = o[0];
+ const isValidColorOption =
+ option !== 'SUPERSET_DEFAULT' && !allColorOptions.includes(option);
+ allColorOptions.push(option);
+ return isValidColorOption;
+ });
+
+ return filteredColorOptions.map(([value]) => {
+ const currentScheme = schemesObject[value];
+
+ // For categorical scheme, display all the colors
+ // For sequential scheme, show 10 or interpolate to 10.
+ // Sequential schemes usually have at most 10 colors.
+ let colors: string[] = [];
+ if (currentScheme) {
+ colors = isLinear
+ ? (currentScheme as SequentialScheme).getColors(10)
+ : currentScheme.colors;
+ }
+ return {
+ customLabel: (
+
+ ),
+ label: schemesObject?.[value]?.label || value,
+ value,
+ };
+ });
+ }, [choices, dashboardId, isLinear, schemes]);
+
+ // We can't pass on change directly because it receives a second
+ // parameter and it would be interpreted as the error parameter
+ const handleOnChange = (value: string) => onChange(value);
+
+ return (
+
+ }
+ />
+ }
+ ariaLabel={t('Select color scheme')}
+ allowClear={clearable}
+ disabled={!!dashboardId}
+ name={`select-${name}`}
+ onChange={handleOnChange}
+ options={options}
+ placeholder={t('Select scheme')}
+ value={currentScheme}
+ />
+ );
+};
+
+export default ColorSchemeControl;