From 331310db9b1e650bf622266c530e21d9f619105a Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Thu, 6 May 2021 02:00:37 -0300 Subject: [PATCH] refactor: Bootstrap to AntD - Form - iteration 2 (#14379) --- .../dashboard/edit_properties.test.ts | 2 +- .../explore/components/BoundsControl_spec.jsx | 4 +- .../explore/components/SaveModal_spec.jsx | 2 - superset-frontend/src/CRUD/Field.jsx | 2 +- .../src/SqlLab/components/SaveQuery.tsx | 2 +- .../SqlLab/components/ScheduleQueryButton.tsx | 2 +- .../src/components/DeleteModal/index.tsx | 2 +- .../{FormLabel/index.tsx => Form/Form.tsx} | 39 ++-- .../FormLabel.test.tsx => Form/FormItem.tsx} | 53 +++--- .../src/components/Form/FormLabel.tsx | 61 +++++++ .../src/components/Form/index.tsx | 23 +++ .../src/components/TableSelector/index.tsx | 2 +- .../components/ColorSchemeControlWrapper.jsx | 4 +- .../components/PropertiesModal/index.jsx | 171 +++++++++--------- .../components/RefreshIntervalModal.tsx | 2 +- .../filterscope/FilterFieldItem.jsx | 2 +- .../src/explore/components/ControlHeader.jsx | 4 +- .../explore/components/EmbedCodeButton.jsx | 2 +- .../components/PropertiesModal/index.tsx | 2 +- .../src/explore/components/SaveModal.tsx | 32 ++-- .../controls/ColorSchemeControl.jsx | 5 +- .../AdhocMetricEditPopover/index.jsx | 2 +- .../components/controls/ViewportControl.jsx | 2 +- .../visualizations/FilterBox/FilterBox.jsx | 2 +- 24 files changed, 245 insertions(+), 179 deletions(-) rename superset-frontend/src/components/{FormLabel/index.tsx => Form/Form.tsx} (57%) rename superset-frontend/src/components/{FormLabel/FormLabel.test.tsx => Form/FormItem.tsx} (50%) create mode 100644 superset-frontend/src/components/Form/FormLabel.tsx create mode 100644 superset-frontend/src/components/Form/index.tsx diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_properties.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_properties.test.ts index 42cde01dd..ad20010a8 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_properties.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_properties.test.ts @@ -91,7 +91,7 @@ describe('Dashboard edit action', () => { cy.get('.ant-modal-body') .should('be.visible') .contains('Title') - .siblings('input') + .get('[data-test="dashboard-title-input"]') .type(`{selectall}{backspace}${dashboardTitle}`); // save edit changes diff --git a/superset-frontend/spec/javascripts/explore/components/BoundsControl_spec.jsx b/superset-frontend/spec/javascripts/explore/components/BoundsControl_spec.jsx index 0af3b5eac..37fac69b0 100644 --- a/superset-frontend/spec/javascripts/explore/components/BoundsControl_spec.jsx +++ b/superset-frontend/spec/javascripts/explore/components/BoundsControl_spec.jsx @@ -16,12 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -/* eslint-disable no-unused-expressions */ import React from 'react'; import { FormControl } from 'react-bootstrap'; import sinon from 'sinon'; -import { mount } from 'enzyme'; - +import { styledMount as mount } from 'spec/helpers/theming'; import BoundsControl from 'src/explore/components/controls/BoundsControl'; const defaultProps = { diff --git a/superset-frontend/spec/javascripts/explore/components/SaveModal_spec.jsx b/superset-frontend/spec/javascripts/explore/components/SaveModal_spec.jsx index 04a30919d..258c725bb 100644 --- a/superset-frontend/spec/javascripts/explore/components/SaveModal_spec.jsx +++ b/superset-frontend/spec/javascripts/explore/components/SaveModal_spec.jsx @@ -24,7 +24,6 @@ import { Provider } from 'react-redux'; import { shallow } from 'enzyme'; import { styledMount as mount } from 'spec/helpers/theming'; -import { FormControl } from 'react-bootstrap'; import { Radio } from 'src/components/Radio'; import Button from 'src/components/Button'; import sinon from 'sinon'; @@ -91,7 +90,6 @@ describe('SaveModal', () => { it('renders a Modal with the right set of components', () => { const wrapper = getWrapper(); expect(wrapper.find(StyledModal)).toExist(); - expect(wrapper.find(FormControl)).toExist(); expect(wrapper.find(Radio)).toHaveLength(2); const footerWrapper = shallow(wrapper.find(StyledModal).props().footer); diff --git a/superset-frontend/src/CRUD/Field.jsx b/superset-frontend/src/CRUD/Field.jsx index 1ed8a5e37..49f377d22 100644 --- a/superset-frontend/src/CRUD/Field.jsx +++ b/superset-frontend/src/CRUD/Field.jsx @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import { FormGroup, HelpBlock, FormControl } from 'react-bootstrap'; import { Tooltip } from 'src/components/Tooltip'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; import './crud.less'; const propTypes = { diff --git a/superset-frontend/src/SqlLab/components/SaveQuery.tsx b/superset-frontend/src/SqlLab/components/SaveQuery.tsx index 7c9edf153..e4e52cf04 100644 --- a/superset-frontend/src/SqlLab/components/SaveQuery.tsx +++ b/superset-frontend/src/SqlLab/components/SaveQuery.tsx @@ -22,7 +22,7 @@ import { FormControl, FormGroup } from 'react-bootstrap'; import { t, supersetTheme, styled } from '@superset-ui/core'; import Button from 'src/components/Button'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; import Modal from 'src/components/Modal'; import Icon from 'src/components/Icon'; diff --git a/superset-frontend/src/SqlLab/components/ScheduleQueryButton.tsx b/superset-frontend/src/SqlLab/components/ScheduleQueryButton.tsx index 24e13cdc9..fc87885f2 100644 --- a/superset-frontend/src/SqlLab/components/ScheduleQueryButton.tsx +++ b/superset-frontend/src/SqlLab/components/ScheduleQueryButton.tsx @@ -23,7 +23,7 @@ import { FormControl, FormGroup } from 'react-bootstrap'; import { t, styled } from '@superset-ui/core'; import * as chrono from 'chrono-node'; import ModalTrigger from 'src/components/ModalTrigger'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; import './ScheduleQueryButton.less'; import Button from 'src/components/Button'; diff --git a/superset-frontend/src/components/DeleteModal/index.tsx b/superset-frontend/src/components/DeleteModal/index.tsx index a4a29fb81..e86d025e6 100644 --- a/superset-frontend/src/components/DeleteModal/index.tsx +++ b/superset-frontend/src/components/DeleteModal/index.tsx @@ -20,7 +20,7 @@ import { t, styled } from '@superset-ui/core'; import React, { useState } from 'react'; import { FormGroup, FormControl, FormControlProps } from 'react-bootstrap'; import Modal from 'src/components/Modal'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; const StyleFormGroup = styled(FormGroup)` padding-top: 8px; diff --git a/superset-frontend/src/components/FormLabel/index.tsx b/superset-frontend/src/components/Form/Form.tsx similarity index 57% rename from superset-frontend/src/components/FormLabel/index.tsx rename to superset-frontend/src/components/Form/Form.tsx index 1b0559897..946324549 100644 --- a/superset-frontend/src/components/FormLabel/index.tsx +++ b/superset-frontend/src/components/Form/Form.tsx @@ -16,32 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -// import { styled } from '@superset-ui/core'; -import React, { ReactNode } from 'react'; -import { ControlLabel } from 'react-bootstrap'; +import React from 'react'; +import AntDForm, { FormProps } from 'antd/lib/form'; +import { styled } from '@superset-ui/core'; -export type FormLabelProps = { - children: ReactNode; - htmlFor?: string; - required?: boolean; - className?: string; -}; +const StyledForm = styled(AntDForm)` + &.ant-form label { + font-size: ${({ theme }) => theme.typography.sizes.s}px; + } + .ant-form-item { + margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + } +`; -export default function FormLabel({ - children, - htmlFor, - required = false, -}: FormLabelProps) { - return ( - <> - - {children} - {required && ( - - * - - )} - - - ); +export default function Form(props: FormProps) { + return ; } diff --git a/superset-frontend/src/components/FormLabel/FormLabel.test.tsx b/superset-frontend/src/components/Form/FormItem.tsx similarity index 50% rename from superset-frontend/src/components/FormLabel/FormLabel.test.tsx rename to superset-frontend/src/components/Form/FormItem.tsx index 0f901785e..ce02c555f 100644 --- a/superset-frontend/src/components/FormLabel/FormLabel.test.tsx +++ b/superset-frontend/src/components/Form/FormItem.tsx @@ -17,36 +17,29 @@ * under the License. */ import React from 'react'; -import { render, screen } from 'spec/helpers/testing-library'; -import { FormControl } from 'react-bootstrap'; -import FormLabel from './index'; +import Form, { FormItemProps } from 'antd/lib/form'; +import { styled } from '@superset-ui/core'; -const mockedProps = { - children: 'Form label', - required: false, - htmlFor: 'form-control', -}; +const StyledItem = styled(Form.Item)` + .ant-form-item-label > label { + text-transform: uppercase; + font-size: ${({ theme }) => theme.typography.sizes.s}px; + color: ${({ theme }) => theme.colors.grayscale.base}; -test('should render', () => { - const { container } = render(); - expect(container).toBeInTheDocument(); -}); + &.ant-form-item-required:not(.ant-form-item-required-mark-optional) { + &::before { + display: none; + } + &::after { + display: inline-block; + color: ${({ theme }) => theme.colors.error.base}; + font-size: ${({ theme }) => theme.typography.sizes.m}px; + content: '*'; + } + } + } +`; -test('should render a Label', () => { - render( - <> - - - , - ); - expect(screen.getByLabelText('Form label')).toBeInTheDocument(); -}); - -test('should be shown as required', () => { - const requiredProps = { - ...mockedProps, - required: true, - }; - render(); - expect(screen.getByText('*').parentNode).toBeInTheDocument(); -}); +export default function FormItem(props: FormItemProps) { + return ; +} diff --git a/superset-frontend/src/components/Form/FormLabel.tsx b/superset-frontend/src/components/Form/FormLabel.tsx new file mode 100644 index 000000000..cb4cba079 --- /dev/null +++ b/superset-frontend/src/components/Form/FormLabel.tsx @@ -0,0 +1,61 @@ +/** + * 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, { ReactNode } from 'react'; +import { styled } from '@superset-ui/core'; + +export type FormLabelProps = { + children: ReactNode; + htmlFor?: string; + required?: boolean; + className?: string; +}; + +const Label = styled.label` + text-transform: uppercase; + font-size: ${({ theme }) => theme.typography.sizes.s}px; + color: ${({ theme }) => theme.colors.grayscale.base}; + margin-bottom: ${({ theme }) => theme.gridUnit}px; +`; + +const RequiredLabel = styled.label` + text-transform: uppercase; + font-size: ${({ theme }) => theme.typography.sizes.s}px; + color: ${({ theme }) => theme.colors.grayscale.base}; + margin-bottom: ${({ theme }) => theme.gridUnit}px; + &::after { + display: inline-block; + color: ${({ theme }) => theme.colors.error.base}; + font-size: ${({ theme }) => theme.typography.sizes.m}px; + content: '*'; + } +`; + +export default function FormLabel({ + children, + htmlFor, + required = false, + className, +}: FormLabelProps) { + const StyledLabel = required ? RequiredLabel : Label; + return ( + + {children} + + ); +} diff --git a/superset-frontend/src/components/Form/index.tsx b/superset-frontend/src/components/Form/index.tsx new file mode 100644 index 000000000..f0734a254 --- /dev/null +++ b/superset-frontend/src/components/Form/index.tsx @@ -0,0 +1,23 @@ +/** + * 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 Form from './Form'; +import FormItem from './FormItem'; +import FormLabel from './FormLabel'; + +export { Form, FormItem, FormLabel }; diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx index f6f4020b2..56d3b2157 100644 --- a/superset-frontend/src/components/TableSelector/index.tsx +++ b/superset-frontend/src/components/TableSelector/index.tsx @@ -25,7 +25,7 @@ import React, { import { styled, SupersetClient, t } from '@superset-ui/core'; import { AsyncSelect, CreatableSelect, Select } from 'src/components/Select'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; import DatabaseSelector from 'src/components/DatabaseSelector'; import RefreshLabel from 'src/components/RefreshLabel'; diff --git a/superset-frontend/src/dashboard/components/ColorSchemeControlWrapper.jsx b/superset-frontend/src/dashboard/components/ColorSchemeControlWrapper.jsx index 871dad648..18af6f5a8 100644 --- a/superset-frontend/src/dashboard/components/ColorSchemeControlWrapper.jsx +++ b/superset-frontend/src/dashboard/components/ColorSchemeControlWrapper.jsx @@ -25,6 +25,7 @@ import ColorSchemeControl from 'src/explore/components/controls/ColorSchemeContr const propTypes = { onChange: PropTypes.func, + labelMargin: PropTypes.number, colorScheme: PropTypes.string, }; @@ -47,13 +48,14 @@ class ColorSchemeControlWrapper extends React.PureComponent { } render() { - const { colorScheme } = this.props; + const { colorScheme, labelMargin = 0 } = this.props; return (

{t('Access')}

- {t('Owners')} - -

- {t( - 'Owners is a list of users who can alter the dashboard. Searchable by name or username.', - )} -

+ + +

+ {t( + 'Owners is a list of users who can alter the dashboard. Searchable by name or username.', + )} +

+

{t('Colors')}

@@ -367,42 +368,44 @@ class PropertiesModal extends React.PureComponent { - {t('Owners')} - -

- {t( - 'Owners is a list of users who can alter the dashboard. Searchable by name or username.', - )} -

+ + +

+ {t( + 'Owners is a list of users who can alter the dashboard. Searchable by name or username.', + )} +

+
- {t('Roles')} - -

- {t( - 'Roles is a list which defines access to the dashboard. Granting a role access to a dashboard will bypass dataset level checks. If no roles defined then the dashboard is available to all roles.', - )} -

+ + +

+ {t( + 'Roles is a list which defines access to the dashboard. Granting a role access to a dashboard will bypass dataset level checks. If no roles defined then the dashboard is available to all roles.', + )} +

+
@@ -410,6 +413,7 @@ class PropertiesModal extends React.PureComponent { @@ -453,7 +457,11 @@ class PropertiesModal extends React.PureComponent { } responsive > -
+

{t('Basic information')}

@@ -461,30 +469,30 @@ class PropertiesModal extends React.PureComponent {
- {t('Title')} - + + + - {t('URL slug')} - -

- {t('A readable URL for your dashboard')} -

+ + +

+ {t('A readable URL for your dashboard')} +

+
{isFeatureEnabled(FeatureFlag.DASHBOARD_RBAC) @@ -504,10 +512,7 @@ class PropertiesModal extends React.PureComponent { {isAdvancedOpen && ( - <> - - {t('JSON metadata')} - + - + )} -
+ ); } diff --git a/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx b/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx index 1e21a453c..aa97ff872 100644 --- a/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx +++ b/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx @@ -23,7 +23,7 @@ import Alert from 'src/components/Alert'; import Button from 'src/components/Button'; import ModalTrigger from 'src/components/ModalTrigger'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; export const options = [ [0, t("Don't refresh")], diff --git a/superset-frontend/src/dashboard/components/filterscope/FilterFieldItem.jsx b/superset-frontend/src/dashboard/components/filterscope/FilterFieldItem.jsx index b42d0f6a3..78364a71e 100644 --- a/superset-frontend/src/dashboard/components/filterscope/FilterFieldItem.jsx +++ b/superset-frontend/src/dashboard/components/filterscope/FilterFieldItem.jsx @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; const propTypes = { label: PropTypes.string.isRequired, diff --git a/superset-frontend/src/explore/components/ControlHeader.jsx b/superset-frontend/src/explore/components/ControlHeader.jsx index 522ac94a0..3e0f91260 100644 --- a/superset-frontend/src/explore/components/ControlHeader.jsx +++ b/superset-frontend/src/explore/components/ControlHeader.jsx @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import { t } from '@superset-ui/core'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; import { Tooltip } from 'src/components/Tooltip'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; const propTypes = { name: PropTypes.string, @@ -85,7 +85,7 @@ export default class ControlHeader extends React.Component { return (
- + {this.props.leftNode && {this.props.leftNode}} { }); } - onSliceNameChange( - event: React.FormEvent & { target: HTMLInputElement }, - ) { + onSliceNameChange(event: React.ChangeEvent) { this.setState({ newSliceName: event.target.value }); } @@ -205,7 +203,7 @@ class SaveModal extends React.Component {
} > -
+
{(this.state.alert || this.props.alert) && ( { } /> )} - + { {' '} {t('Save as ...')}   - +
- - {t('Chart name')} - + - - - {t('Add to dashboard')} + + { /> } /> - -
+ + ); } diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx index 908d80987..6b0d2871e 100644 --- a/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx +++ b/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx @@ -28,6 +28,7 @@ import './ColorSchemeControl.less'; const propTypes = { description: PropTypes.string, label: PropTypes.string.isRequired, + labelMargin: PropTypes.number, name: PropTypes.string.isRequired, onChange: PropTypes.func, value: PropTypes.string, @@ -92,7 +93,7 @@ export default class ColorSchemeControl extends React.PureComponent { } render() { - const { schemes, choices } = this.props; + const { schemes, choices, labelMargin = 0 } = this.props; // save parsed schemes for later this.schemes = isFunction(schemes) ? schemes() : schemes; const options = (isFunction(choices) ? choices() : choices).map( @@ -118,7 +119,7 @@ export default class ColorSchemeControl extends React.PureComponent { return (
-
); } diff --git a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx index d214dc3a5..848ce8283 100644 --- a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx +++ b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx @@ -25,7 +25,7 @@ import Button from 'src/components/Button'; import { NativeSelect as Select } from 'src/components/Select'; import { t, styled } from '@superset-ui/core'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; import { SQLEditor } from 'src/components/AsyncAceEditor'; import sqlKeywords from 'src/SqlLab/utils/sqlKeywords'; import { noOp } from 'src/utils/common'; diff --git a/superset-frontend/src/explore/components/controls/ViewportControl.jsx b/superset-frontend/src/explore/components/controls/ViewportControl.jsx index 85404ffb0..631a8c550 100644 --- a/superset-frontend/src/explore/components/controls/ViewportControl.jsx +++ b/superset-frontend/src/explore/components/controls/ViewportControl.jsx @@ -22,7 +22,7 @@ import Popover from 'src/components/Popover'; import { decimal2sexagesimal } from 'geolib'; import Label from 'src/components/Label'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; import TextControl from './TextControl'; import ControlHeader from '../ControlHeader'; diff --git a/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx b/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx index 450d51400..cbe95664e 100644 --- a/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx +++ b/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx @@ -29,7 +29,7 @@ import { BOOL_TRUE_DISPLAY, SLOW_DEBOUNCE, } from 'src/constants'; -import FormLabel from 'src/components/FormLabel'; +import { FormLabel } from 'src/components/Form'; import DateFilterControl from 'src/explore/components/controls/DateFilterControl'; import ControlRow from 'src/explore/components/ControlRow'; import Control from 'src/explore/components/Control';