Skip to content

Commit

Permalink
Add NoDataAlert widget (#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio Clebal authored Oct 1, 2021
1 parent fdd000f commit af244af
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 73 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Not released

- Add NoDataAlert widget [#188](https://github.com/CartoDB/carto-react/pull/188)
- Update echarts to v5 [#167](https://github.com/CartoDB/carto-react/pull/167)
- Fix unnecessary widget calculations triggered by widget itself [#185](https://github.com/CartoDB/carto-react/pull/185)
- Add new `useSourceFilters` hook for better custom widgets dev [#185](https://github.com/CartoDB/carto-react/pull/185)
Expand Down
10 changes: 0 additions & 10 deletions packages/react-ui/__tests__/widgets/CategoryWidgetUI.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,6 @@ animateValues.mockImplementation(({ end, drawFrame }) => {
});

describe('CategoryWidgetUI', () => {
test('empty', () => {
render(<CategoryWidgetUI data={[]} />);

const NO_DATA_TEXT = /No data available/;
const NO_RESULT_TEXT = /There are no results for the combination of filters applied to your data. Try tweaking your filters, or zoom and pan the map to adjust the Map View./;

expect(screen.getByText(NO_DATA_TEXT)).toBeInTheDocument();
expect(screen.getByText(NO_RESULT_TEXT)).toBeInTheDocument();
});

test('item skeleton should display', () => {
const { container } = render(<CategoryWidgetUI data={[]} isLoading={true} />);

Expand Down
22 changes: 3 additions & 19 deletions packages/react-ui/src/widgets/CategoryWidgetUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
Typography,
makeStyles
} from '@material-ui/core';
import { Alert, AlertTitle, Skeleton } from '@material-ui/lab';
import { Skeleton } from '@material-ui/lab';

import { animateValues } from './utils/animations';

Expand Down Expand Up @@ -128,15 +128,7 @@ const SearchIcon = () => (
);

function CategoryWidgetUI(props) {
const {
data,
formatter,
labels,
isLoading,
maxItems,
order,
selectedCategories
} = props;
const { data, formatter, labels, maxItems, order, selectedCategories } = props;
const [sortedData, setSortedData] = useState([]);
const [maxValue, setMaxValue] = useState(1);
const [showAll, setShowAll] = useState(false);
Expand Down Expand Up @@ -436,7 +428,7 @@ function CategoryWidgetUI(props) {

return (
<div className={classes.root}>
{data && (data.length > 0 || !isLoading) ? (
{data?.length > 0 ? (
<>
{sortedData.length > 0 && (
<Grid
Expand Down Expand Up @@ -509,12 +501,6 @@ function CategoryWidgetUI(props) {
}
/>
))
) : data.length === 0 && !isLoading ? (
<Alert severity='warning'>
<AlertTitle>No data available</AlertTitle>
There are no results for the combination of filters applied to your data.
Try tweaking your filters, or zoom and pan the map to adjust the Map View.
</Alert>
) : (
<>
<Typography variant='body2'>No results</Typography>
Expand Down Expand Up @@ -557,7 +543,6 @@ CategoryWidgetUI.defaultProps = {
data: null,
formatter: (v) => v,
labels: {},
isLoading: false,
maxItems: 5,
order: CategoryWidgetUI.ORDER_TYPES.RANKING,
selectedCategories: []
Expand All @@ -572,7 +557,6 @@ CategoryWidgetUI.propTypes = {
),
formatter: PropTypes.func,
labels: PropTypes.object,
isLoading: PropTypes.bool,
maxItems: PropTypes.number,
selectedCategories: PropTypes.array,
onSelectedCategoriesChange: PropTypes.func,
Expand Down
42 changes: 42 additions & 0 deletions packages/react-ui/storybook/stories/widgets/NoDataAlert.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import NoDataAlert from '../../../../react-widgets/src/widgets/NoDataAlert';
import { buildReactPropsAsString } from '../../utils';

const options = {
title: 'Widgets/NoDataAlert',
component: NoDataAlert,
argTypes: {
title: {
table: {
type: {
summary: 'string'
}
},
control: { type: 'text' }
},
body: {
table: {
type: {
summary: 'string'
}
},
control: { type: 'text' }
}
}
};

export default options;

const Template = (args) => <NoDataAlert {...args} />;

export const Empty = Template.bind({});
Empty.args = {};
Empty.parameters = buildReactPropsAsString({}, 'NoDataAlert');

export const CustomTexts = Template.bind({});
const CustomTextsProps = {
titlee: 'Example',
body: "Hey, I've modified the NoDataAlert component"
};
CustomTexts.args = CustomTextsProps;
CustomTexts.parameters = buildReactPropsAsString(CustomTextsProps, 'NoDataAlert');
1 change: 1 addition & 0 deletions packages/react-widgets/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export {
getScatter
} from './models';
export { default as useSourceFilters } from './hooks/useSourceFilters';
export { default as NoDataAlert } from './widgets/NoDataAlert';
1 change: 1 addition & 0 deletions packages/react-widgets/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export {
getScatter
} from './models';
export { default as useSourceFilters } from './hooks/useSourceFilters';
export { default as NoDataAlert } from './widgets/NoDataAlert';
5 changes: 5 additions & 0 deletions packages/react-widgets/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ export type ScatterPlotWidget = {
export type useSourceFilters = {
dataSource: string,
id: string,
};

export type NoDataAlert = {
title: string,
body: string,
};
32 changes: 20 additions & 12 deletions packages/react-widgets/src/widgets/CategoryWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { _FilterTypes as FilterTypes, AggregationTypes } from '@carto/react-core
import { getCategories } from '../models';
import useSourceFilters from '../hooks/useSourceFilters';
import { selectIsViewportFeaturesReadyForSource } from '@carto/react-redux/';
import NoDataAlert from './NoDataAlert';

/**
* Renders a <CategoryWidget /> component
Expand All @@ -21,6 +22,7 @@ import { selectIsViewportFeaturesReadyForSource } from '@carto/react-redux/';
* @param {Object} [props.labels] - Overwrite category labels
* @param {Function} [props.onError] - Function to handle error messages from the widget.
* @param {Object} [props.wrapperProps] - Extra props to pass to [WrapperWidgetUI](https://storybook-react.carto.com/?path=/docs/widgets-wrapperwidgetui--default)
* @param {Object} [props.noDataAlertProps] - Extra props to pass to [NoDataAlert]()
*/
function CategoryWidget(props) {
const {
Expand All @@ -33,15 +35,16 @@ function CategoryWidget(props) {
formatter,
labels,
onError,
wrapperProps
wrapperProps,
noDataAlertProps
} = props;
const dispatch = useDispatch();

const isSourceReady = useSelector((state) =>
selectIsViewportFeaturesReadyForSource(state, dataSource)
);

const [categoryData, setCategoryData] = useState(null);
const [categoryData, setCategoryData] = useState([]);
const [selectedCategories, setSelectedCategories] = useState([]);
const [isLoading, setIsLoading] = useState(true);

Expand Down Expand Up @@ -109,14 +112,17 @@ function CategoryWidget(props) {

return (
<WrapperWidgetUI title={title} isLoading={isLoading} {...wrapperProps}>
<CategoryWidgetUI
data={categoryData}
formatter={formatter}
labels={labels}
isLoading={isLoading}
selectedCategories={selectedCategories}
onSelectedCategoriesChange={handleSelectedCategoriesChange}
/>
{categoryData.length || isLoading ? (
<CategoryWidgetUI
data={categoryData}
formatter={formatter}
labels={labels}
selectedCategories={selectedCategories}
onSelectedCategoriesChange={handleSelectedCategoriesChange}
/>
) : (
<NoDataAlert {...noDataAlertProps} />
)}
</WrapperWidgetUI>
);
}
Expand All @@ -131,12 +137,14 @@ CategoryWidget.propTypes = {
formatter: PropTypes.func,
labels: PropTypes.object,
onError: PropTypes.func,
wrapperProps: PropTypes.object
wrapperProps: PropTypes.object,
noDataAlertProps: PropTypes.object
};

CategoryWidget.defaultProps = {
labels: {},
wrapperProps: {}
wrapperProps: {},
noDataAlertProps: {}
};

export default CategoryWidget;
35 changes: 22 additions & 13 deletions packages/react-widgets/src/widgets/HistogramWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { getHistogram } from '../models';
import useSourceFilters from '../hooks/useSourceFilters';
import { selectIsViewportFeaturesReadyForSource } from '@carto/react-redux/';
import NoDataAlert from './NoDataAlert';

/**
* Renders a <HistogramWidget /> component
Expand All @@ -26,6 +27,7 @@ import { selectIsViewportFeaturesReadyForSource } from '@carto/react-redux/';
* @param {boolean} [props.tooltip=true] - Whether to show a tooltip or not
* @param {Function} [props.onError] - Function to handle error messages from the widget.
* @param {Object} [props.wrapperProps] - Extra props to pass to [WrapperWidgetUI](https://storybook-react.carto.com/?path=/docs/widgets-wrapperwidgetui--default)
* @param {Object} [props.noDataAlertProps] - Extra props to pass to [NoDataAlert]()
*/
function HistogramWidget(props) {
const {
Expand All @@ -40,7 +42,8 @@ function HistogramWidget(props) {
formatter,
tooltip,
onError,
wrapperProps
wrapperProps,
noDataAlertProps
} = props;
const dispatch = useDispatch();

Expand Down Expand Up @@ -133,16 +136,20 @@ function HistogramWidget(props) {

return (
<WrapperWidgetUI title={title} {...wrapperProps} isLoading={isLoading}>
<HistogramWidgetUI
data={histogramData}
dataAxis={dataAxis || [...ticks, `> ${ticks[ticks.length - 1]}`]}
selectedBars={selectedBars}
onSelectedBarsChange={handleSelectedBarsChange}
tooltip={tooltip}
tooltipFormatter={tooltipFormatter}
xAxisFormatter={xAxisFormatter}
yAxisFormatter={formatter}
/>
{histogramData.length || isLoading ? (
<HistogramWidgetUI
data={histogramData}
dataAxis={dataAxis || [...ticks, `> ${ticks[ticks.length - 1]}`]}
selectedBars={selectedBars}
onSelectedBarsChange={handleSelectedBarsChange}
tooltip={tooltip}
tooltipFormatter={tooltipFormatter}
xAxisFormatter={xAxisFormatter}
yAxisFormatter={formatter}
/>
) : (
<NoDataAlert {...noDataAlertProps} />
)}
</WrapperWidgetUI>
);
}
Expand All @@ -158,12 +165,14 @@ HistogramWidget.propTypes = {
tooltip: PropTypes.bool,
ticks: PropTypes.array.isRequired,
onError: PropTypes.func,
wrapperProps: PropTypes.object
wrapperProps: PropTypes.object,
noDataAlertProps: PropTypes.object
};

HistogramWidget.defaultProps = {
tooltip: true,
wrapperProps: {}
wrapperProps: {},
noDataAlertProps: {}
};

export default HistogramWidget;
17 changes: 17 additions & 0 deletions packages/react-widgets/src/widgets/NoDataAlert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { Alert, AlertTitle } from '@material-ui/lab';
import { Box } from '@material-ui/core';

function NoDataAlert({
title = 'No data available',
body = 'There are no results for the combination of filters applied to your data. Try tweaking your filters, or zoom and pan the map to adjust the Map View.'
}) {
return (
<Alert severity='warning'>
{title && <AlertTitle>{title}</AlertTitle>}
{body || <Box mt={-1} />}
</Alert>
);
}

export default NoDataAlert;
31 changes: 20 additions & 11 deletions packages/react-widgets/src/widgets/PieWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { getCategories } from '../models';
import useSourceFilters from '../hooks/useSourceFilters';
import { selectIsViewportFeaturesReadyForSource } from '@carto/react-redux/';
import NoDataAlert from './NoDataAlert';

/**
* Renders a <PieWidget /> component
Expand All @@ -26,6 +27,7 @@ import { selectIsViewportFeaturesReadyForSource } from '@carto/react-redux/';
* @param {string} props.height - Height of the chart
* @param {Function} [props.onError] - Function to handle error messages from the widget.
* @param {Object} [props.wrapperProps] - Extra props to pass to [WrapperWidgetUI](https://storybook-react.carto.com/?path=/docs/widgets-wrapperwidgetui--default)
* @param {Object} [props.noDataAlertProps] - Extra props to pass to [NoDataAlert]()
*/
function PieWidget({
id,
Expand All @@ -38,7 +40,8 @@ function PieWidget({
formatter,
tooltipFormatter,
onError,
wrapperProps
wrapperProps,
noDataAlertProps
}) {
const dispatch = useDispatch();

Expand Down Expand Up @@ -113,14 +116,18 @@ function PieWidget({

return (
<WrapperWidgetUI title={title} isLoading={isLoading} {...wrapperProps}>
<PieWidgetUI
data={categoryData}
formatter={formatter}
height={height}
tooltipFormatter={tooltipFormatter}
selectedCategories={selectedCategories}
onSelectedCategoriesChange={handleSelectedCategoriesChange}
/>
{categoryData.length || isLoading ? (
<PieWidgetUI
data={categoryData}
formatter={formatter}
height={height}
tooltipFormatter={tooltipFormatter}
selectedCategories={selectedCategories}
onSelectedCategoriesChange={handleSelectedCategoriesChange}
/>
) : (
<NoDataAlert {...noDataAlertProps} />
)}
</WrapperWidgetUI>
);
}
Expand All @@ -136,11 +143,13 @@ PieWidget.propTypes = {
formatter: PropTypes.func,
tooltipFormatter: PropTypes.func,
onError: PropTypes.func,
wrapperProps: PropTypes.object
wrapperProps: PropTypes.object,
noDataAlertProps: PropTypes.object
};

PieWidget.defaultProps = {
wrapperProps: {}
wrapperProps: {},
noDataAlertProps: {}
};

export default PieWidget;
Loading

0 comments on commit af244af

Please sign in to comment.