From dd0cb31eca5974e9c0e122a38c7a106867aad17c Mon Sep 17 00:00:00 2001 From: Adam Dobrawy Date: Mon, 31 Jan 2022 19:03:39 +0100 Subject: [PATCH] refactor: upgrade ControlHeader to TSX & FC and add storybook (#18188) * refactor: upgrade ControlHeader to TSX & FC and add storybook * fix: Add story for error in ControlHeader * apply review comments --- .../src/explore/components/ControlHeader.jsx | 174 ------------------ .../components/ControlHeader.stories.tsx | 77 ++++++++ .../src/explore/components/ControlHeader.tsx | 158 ++++++++++++++++ 3 files changed, 235 insertions(+), 174 deletions(-) delete mode 100644 superset-frontend/src/explore/components/ControlHeader.jsx create mode 100644 superset-frontend/src/explore/components/ControlHeader.stories.tsx create mode 100644 superset-frontend/src/explore/components/ControlHeader.tsx 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;