diff --git a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts index 97dfd2945a..e2aec6b7b9 100644 --- a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts @@ -122,12 +122,12 @@ describe('Test datatable', () => { }); it('Data Pane opens and loads results', () => { cy.contains('Results').click(); - cy.get('[data-test="row-count-label"]').contains('26 rows retrieved'); + cy.get('[data-test="row-count-label"]').contains('26 rows'); cy.get('.ant-empty-description').should('not.exist'); }); it('Datapane loads view samples', () => { cy.contains('Samples').click(); - cy.get('[data-test="row-count-label"]').contains('1k rows retrieved'); + cy.get('[data-test="row-count-label"]').contains('1k rows'); cy.get('.ant-empty-description').should('not.exist'); }); }); diff --git a/superset-frontend/src/components/AlteredSliceTag/index.jsx b/superset-frontend/src/components/AlteredSliceTag/index.jsx index e4a0d1bdeb..2faa62c890 100644 --- a/superset-frontend/src/components/AlteredSliceTag/index.jsx +++ b/superset-frontend/src/components/AlteredSliceTag/index.jsx @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { isEqual, isEmpty } from 'lodash'; -import { t } from '@superset-ui/core'; +import { styled, t } from '@superset-ui/core'; import { sanitizeFormData } from 'src/explore/exploreUtils/formData'; import getControlsForVizType from 'src/utils/getControlsForVizType'; import { safeStringify } from 'src/utils/safeStringify'; @@ -32,6 +32,18 @@ const propTypes = { currentFormData: PropTypes.object.isRequired, }; +const StyledLabel = styled.span` + ${({ theme }) => ` + font-size: ${theme.typography.sizes.s}px; + color: ${theme.colors.grayscale.dark1}; + background-color: ${theme.colors.alert.base}; + + &: hover { + background-color: ${theme.colors.alert.dark1}; + } + `} +`; + function alterForComparison(value) { // Considering `[]`, `{}`, `null` and `undefined` as identical // for this purpose @@ -183,9 +195,7 @@ export default class AlteredSliceTag extends React.Component { renderTriggerNode() { return ( - - {t('Altered')} - + {t('Altered')} ); } diff --git a/superset-frontend/src/components/CachedLabel/index.tsx b/superset-frontend/src/components/CachedLabel/index.tsx index a401ab1e0e..a6c74cb8eb 100644 --- a/superset-frontend/src/components/CachedLabel/index.tsx +++ b/superset-frontend/src/components/CachedLabel/index.tsx @@ -48,7 +48,7 @@ const CacheLabel: React.FC = ({ onMouseOver={() => setHovered(true)} onMouseOut={() => setHovered(false)} > - {t('cached')} + {t('Cached')} ); diff --git a/superset-frontend/src/components/Label/Label.stories.tsx b/superset-frontend/src/components/Label/Label.stories.tsx index 07fc7c09fa..9363cc80d3 100644 --- a/superset-frontend/src/components/Label/Label.stories.tsx +++ b/superset-frontend/src/components/Label/Label.stories.tsx @@ -26,8 +26,9 @@ export default { excludeStories: 'options', }; -export const options = [ +export const options: Type[] = [ 'default', + 'alert', 'info', 'success', 'warning', diff --git a/superset-frontend/src/components/Label/index.tsx b/superset-frontend/src/components/Label/index.tsx index e66437da7d..eb2aff41d0 100644 --- a/superset-frontend/src/components/Label/index.tsx +++ b/superset-frontend/src/components/Label/index.tsx @@ -23,6 +23,7 @@ import { useTheme } from '@superset-ui/core'; export type OnClickHandler = React.MouseEventHandler; export type Type = + | 'alert' | 'success' | 'warning' | 'danger' @@ -44,9 +45,17 @@ export interface LabelProps extends React.HTMLAttributes { export default function Label(props: LabelProps) { const theme = useTheme(); const { colors, transitionTiming } = theme; - const { type, onClick, children, ...rest } = props; - const { primary, secondary, grayscale, success, warning, error, info } = - colors; + const { type = 'default', onClick, children, ...rest } = props; + const { + alert, + primary, + secondary, + grayscale, + success, + warning, + error, + info, + } = colors; let backgroundColor = grayscale.light3; let backgroundColorHover = onClick ? primary.light2 : grayscale.light3; @@ -54,11 +63,14 @@ export default function Label(props: LabelProps) { let borderColorHover = onClick ? primary.light1 : 'transparent'; let color = grayscale.dark1; - if (type && type !== 'default') { + if (type !== 'default') { color = grayscale.light4; let baseColor; - if (type === 'success') { + if (type === 'alert') { + color = grayscale.dark1; + baseColor = alert; + } else if (type === 'success') { baseColor = success; } else if (type === 'warning') { baseColor = warning; diff --git a/superset-frontend/src/explore/components/DataTableControl/RowCount.test.tsx b/superset-frontend/src/explore/components/DataTableControl/RowCount.test.tsx index be3f0e980c..6ebbcef77b 100644 --- a/superset-frontend/src/explore/components/DataTableControl/RowCount.test.tsx +++ b/superset-frontend/src/explore/components/DataTableControl/RowCount.test.tsx @@ -20,9 +20,14 @@ import React from 'react'; import { render, screen } from 'spec/helpers/testing-library'; import { RowCount } from '.'; -test('Render a RowCount', () => { +test('Render a RowCount with a single row', () => { + render(); + expect(screen.getByText('1 row')).toBeInTheDocument(); +}); + +test('Render a RowCount with multiple rows', () => { render(); - expect(screen.getByText('3 rows retrieved')).toBeInTheDocument(); + expect(screen.getByText('3 rows')).toBeInTheDocument(); }); test('Render a RowCount on loading', () => { diff --git a/superset-frontend/src/explore/components/DataTableControl/index.tsx b/superset-frontend/src/explore/components/DataTableControl/index.tsx index 7a25c374bd..791f5f8ff3 100644 --- a/superset-frontend/src/explore/components/DataTableControl/index.tsx +++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx @@ -127,13 +127,7 @@ export const RowCount = ({ }: { data?: Record[]; loading: boolean; -}) => ( - -); +}) => ; enum FormatPickerValue { Formatted, diff --git a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx index 786150449e..04869f5f10 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx @@ -103,7 +103,7 @@ describe('DataTablesPane', () => { useRedux: true, }); userEvent.click(screen.getByText('Results')); - expect(await screen.findByText('0 rows retrieved')).toBeVisible(); + expect(await screen.findByText('0 rows')).toBeVisible(); expect(await screen.findByLabelText('Collapse data panel')).toBeVisible(); localStorage.clear(); }); @@ -114,7 +114,7 @@ describe('DataTablesPane', () => { useRedux: true, }); userEvent.click(screen.getByText('Samples')); - expect(await screen.findByText('0 rows retrieved')).toBeVisible(); + expect(await screen.findByText('0 rows')).toBeVisible(); expect(await screen.findByLabelText('Collapse data panel')).toBeVisible(); }); @@ -158,7 +158,7 @@ describe('DataTablesPane', () => { }, ); userEvent.click(screen.getByText('Results')); - expect(await screen.findByText('1 rows retrieved')).toBeVisible(); + expect(await screen.findByText('1 row')).toBeVisible(); userEvent.click(screen.getByLabelText('Copy')); expect(copyToClipboardSpy).toHaveBeenCalledWith( @@ -210,7 +210,7 @@ describe('DataTablesPane', () => { }, ); userEvent.click(screen.getByText('Results')); - expect(await screen.findByText('2 rows retrieved')).toBeVisible(); + expect(await screen.findByText('2 rows')).toBeVisible(); expect(screen.getByText('Action')).toBeVisible(); expect(screen.getByText('Horror')).toBeVisible(); diff --git a/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.stories.tsx b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.stories.tsx index 716830f9ca..558f14f308 100644 --- a/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.stories.tsx +++ b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.stories.tsx @@ -17,17 +17,21 @@ * under the License. */ import React from 'react'; -import RowCountLabel from '.'; +import RowCountLabel, { RowCountLabelProps } from '.'; export default { title: 'RowCountLabel', component: RowCountLabel, }; -const options = { +const options: { [key in string]: RowCountLabelProps } = { loading: { loading: true, }, + single: { + rowcount: 1, + limit: 100, + }, full: { rowcount: 100, limit: 100, @@ -36,10 +40,6 @@ const options = { rowcount: 50, limit: 100, }, - suffix: { - rowcount: 1, - suffix: 'suffix', - }, }; export const RowCountLabelGallery = () => ( @@ -51,7 +51,6 @@ export const RowCountLabelGallery = () => ( loading={options[name].loading} rowcount={options[name].rowcount} limit={options[name].limit} - suffix={options[name].suffix} /> ))} diff --git a/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.jsx b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.jsx deleted file mode 100644 index 452f68a745..0000000000 --- a/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.jsx +++ /dev/null @@ -1,50 +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 { shallow } from 'enzyme'; - -import Label from 'src/components/Label'; -import { Tooltip } from 'src/components/Tooltip'; -import RowCountLabel from '.'; - -describe('RowCountLabel', () => { - const defaultProps = { - rowcount: 51, - limit: 100, - }; - - it('is valid', () => { - expect(React.isValidElement()).toBe( - true, - ); - }); - it('renders a Label and a Tooltip', () => { - const wrapper = shallow(); - expect(wrapper.find(Label)).toExist(); - expect(wrapper.find(Tooltip)).toExist(); - }); - it('renders a danger when limit is reached', () => { - const props = { - rowcount: 100, - limit: 100, - }; - const wrapper = shallow(); - expect(wrapper.find(Label).first().props().type).toBe('danger'); - }); -}); diff --git a/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.tsx b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.tsx new file mode 100644 index 0000000000..2e7a44c15e --- /dev/null +++ b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.tsx @@ -0,0 +1,65 @@ +/** + * 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 { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; + +import RowCountLabel from '.'; + +test('RowCountLabel renders singular result', () => { + render(); + const expectedText = '1 row'; + expect(screen.getByText(expectedText)).toBeInTheDocument(); + userEvent.hover(screen.getByText(expectedText)); + expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); +}); + +test('RowCountLabel renders plural result', () => { + render(); + const expectedText = '2 rows'; + expect(screen.getByText(expectedText)).toBeInTheDocument(); + userEvent.hover(screen.getByText(expectedText)); + expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); +}); + +test('RowCountLabel renders formatted result', () => { + render(); + const expectedText = '1k rows'; + expect(screen.getByText(expectedText)).toBeInTheDocument(); + userEvent.hover(screen.getByText(expectedText)); + expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); +}); + +test('RowCountLabel renders limit with danger and tooltip', async () => { + render(); + const expectedText = '100 rows'; + expect(screen.getByText(expectedText)).toBeInTheDocument(); + userEvent.hover(screen.getByText(expectedText)); + const tooltip = await screen.findByRole('tooltip'); + expect(tooltip).toHaveTextContent('Limit reached'); + expect(tooltip).toHaveStyle('background: rgba(0, 0, 0, 0.902);'); +}); + +test('RowCountLabel renders loading', () => { + render(); + const expectedText = 'Loading...'; + expect(screen.getByText(expectedText)).toBeInTheDocument(); + userEvent.hover(screen.getByText(expectedText)); + expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); +}); diff --git a/superset-frontend/src/explore/components/RowCountLabel/index.tsx b/superset-frontend/src/explore/components/RowCountLabel/index.tsx index c612856817..3597e49724 100644 --- a/superset-frontend/src/explore/components/RowCountLabel/index.tsx +++ b/superset-frontend/src/explore/components/RowCountLabel/index.tsx @@ -17,36 +17,36 @@ * under the License. */ import React from 'react'; -import { getNumberFormatter, t } from '@superset-ui/core'; +import { getNumberFormatter, t, tn } from '@superset-ui/core'; import Label from 'src/components/Label'; import { Tooltip } from 'src/components/Tooltip'; type RowCountLabelProps = { - rowcount: number; + rowcount?: number; limit?: number; - suffix?: string; loading?: boolean; }; export default function RowCountLabel(props: RowCountLabelProps) { - const { rowcount = 0, limit, suffix = t('rows'), loading } = props; + const { rowcount = 0, limit, loading } = props; const limitReached = rowcount === limit; const type = limitReached || (rowcount === 0 && !loading) ? 'danger' : 'default'; const formattedRowCount = getNumberFormatter()(rowcount); - const tooltip = ( - - {limitReached &&
{t('Limit reached')}
} - {loading ? 'Loading' : rowcount} -
+ const label = ( + ); - return ( - - + return limitReached ? ( + {t('Limit reached')}}> + {label} + ) : ( + label ); }