Merge pull request #273 from acelaya-forks/feature/charts-improvements

Feature/charts improvements
This commit is contained in:
Alejandro Celaya 2020-05-31 09:19:02 +02:00 committed by GitHub
commit c98b28ff0f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 28 additions and 15 deletions

View file

@ -13,10 +13,11 @@ const propTypes = {
stats: PropTypes.object, stats: PropTypes.object,
max: PropTypes.number, max: PropTypes.number,
highlightedStats: PropTypes.object, highlightedStats: PropTypes.object,
highlightedLabel: PropTypes.string,
onClick: PropTypes.func, onClick: PropTypes.func,
}; };
const generateGraphData = (title, isBarChart, labels, data, highlightedData) => ({ const generateGraphData = (title, isBarChart, labels, data, highlightedData, highlightedLabel) => ({
labels, labels,
datasets: [ datasets: [
{ {
@ -41,7 +42,7 @@ const generateGraphData = (title, isBarChart, labels, data, highlightedData) =>
}, },
highlightedData && { highlightedData && {
title, title,
label: 'Selected', label: highlightedLabel || 'Selected',
data: highlightedData, data: highlightedData,
backgroundColor: 'rgba(247, 127, 40, 0.4)', backgroundColor: 'rgba(247, 127, 40, 0.4)',
borderColor: '#F77F28', borderColor: '#F77F28',
@ -60,7 +61,7 @@ const determineHeight = (isBarChart, labels) => {
return isBarChart && labels.length > 20 ? labels.length * 8 : null; return isBarChart && labels.length > 20 ? labels.length * 8 : null;
}; };
const renderGraph = (title, isBarChart, stats, max, highlightedStats, onClick) => { const renderGraph = (title, isBarChart, stats, max, highlightedStats, highlightedLabel, onClick) => {
const hasHighlightedStats = highlightedStats && Object.keys(highlightedStats).length > 0; const hasHighlightedStats = highlightedStats && Object.keys(highlightedStats).length > 0;
const Component = isBarChart ? HorizontalBar : Doughnut; const Component = isBarChart ? HorizontalBar : Doughnut;
const labels = keys(stats).map(dropLabelIfHidden); const labels = keys(stats).map(dropLabelIfHidden);
@ -94,7 +95,7 @@ const renderGraph = (title, isBarChart, stats, max, highlightedStats, onClick) =
target.style.cursor = chartElement[0] ? 'pointer' : 'default'; target.style.cursor = chartElement[0] ? 'pointer' : 'default';
}), }),
}; };
const graphData = generateGraphData(title, isBarChart, labels, data, highlightedData); const graphData = generateGraphData(title, isBarChart, labels, data, highlightedData, highlightedLabel);
const height = determineHeight(isBarChart, labels); const height = determineHeight(isBarChart, labels);
// Provide a key based on the height, so that every time the dataset changes, a new graph is rendered // Provide a key based on the height, so that every time the dataset changes, a new graph is rendered
@ -118,10 +119,10 @@ const renderGraph = (title, isBarChart, stats, max, highlightedStats, onClick) =
); );
}; };
const GraphCard = ({ title, footer, isBarChart, stats, max, highlightedStats, onClick }) => ( const GraphCard = ({ title, footer, isBarChart, stats, max, highlightedStats, highlightedLabel, onClick }) => (
<Card> <Card>
<CardHeader className="graph-card__header">{typeof title === 'function' ? title() : title}</CardHeader> <CardHeader className="graph-card__header">{typeof title === 'function' ? title() : title}</CardHeader>
<CardBody>{renderGraph(title, isBarChart, stats, max, highlightedStats, onClick)}</CardBody> <CardBody>{renderGraph(title, isBarChart, stats, max, highlightedStats, highlightedLabel, onClick)}</CardBody>
{footer && <CardFooter className="graph-card__footer--sticky">{footer}</CardFooter>} {footer && <CardFooter className="graph-card__footer--sticky">{footer}</CardFooter>}
</Card> </Card>
); );

View file

@ -17,6 +17,7 @@ export default class SortableBarGraph extends React.Component {
static propTypes = { static propTypes = {
stats: PropTypes.object.isRequired, stats: PropTypes.object.isRequired,
highlightedStats: PropTypes.object, highlightedStats: PropTypes.object,
highlightedLabel: PropTypes.string,
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
sortingItems: PropTypes.object.isRequired, sortingItems: PropTypes.object.isRequired,
extraHeaderContent: PropTypes.func, extraHeaderContent: PropTypes.func,

View file

@ -42,6 +42,7 @@ const VisitsStats = ({ processStatsFromVisits, normalizeVisits }, OpenMapModalBt
const [ showTable, toggleTable ] = useToggle(); const [ showTable, toggleTable ] = useToggle();
const [ tableIsSticky, , setSticky, unsetSticky ] = useToggle(); const [ tableIsSticky, , setSticky, unsetSticky ] = useToggle();
const [ highlightedVisits, setHighlightedVisits ] = useState([]); const [ highlightedVisits, setHighlightedVisits ] = useState([]);
const [ highlightedLabel, setHighlightedLabel ] = useState();
const [ isMobileDevice, setIsMobileDevice ] = useState(false); const [ isMobileDevice, setIsMobileDevice ] = useState(false);
const determineIsMobileDevice = () => setIsMobileDevice(matchMedia('(max-width: 991px)').matches); const determineIsMobileDevice = () => setIsMobileDevice(matchMedia('(max-width: 991px)').matches);
const setSelectedVisits = (selectedVisits) => { const setSelectedVisits = (selectedVisits) => {
@ -53,9 +54,11 @@ const VisitsStats = ({ processStatsFromVisits, normalizeVisits }, OpenMapModalBt
if (selectedBar === newSelectedBar) { if (selectedBar === newSelectedBar) {
setHighlightedVisits([]); setHighlightedVisits([]);
setHighlightedLabel(undefined);
selectedBar = undefined; selectedBar = undefined;
} else { } else {
setHighlightedVisits(normalizedVisits.filter(propEq(prop, value))); setHighlightedVisits(normalizedVisits.filter(propEq(prop, value)));
setHighlightedLabel(value);
selectedBar = newSelectedBar; selectedBar = newSelectedBar;
} }
}; };
@ -111,7 +114,12 @@ const VisitsStats = ({ processStatsFromVisits, normalizeVisits }, OpenMapModalBt
return ( return (
<div className="row"> <div className="row">
<div className="col-12 mt-4"> <div className="col-12 mt-4">
<LineChartCard title="Visits during time" visits={visits} highlightedVisits={highlightedVisits} /> <LineChartCard
title="Visits during time"
visits={visits}
highlightedVisits={highlightedVisits}
highlightedLabel={highlightedLabel}
/>
</div> </div>
<div className="col-xl-4 col-lg-6 mt-4"> <div className="col-xl-4 col-lg-6 mt-4">
<GraphCard title="Operating systems" stats={os} /> <GraphCard title="Operating systems" stats={os} />
@ -125,6 +133,7 @@ const VisitsStats = ({ processStatsFromVisits, normalizeVisits }, OpenMapModalBt
stats={referrers} stats={referrers}
withPagination={false} withPagination={false}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'referer')} highlightedStats={highlightedVisitsToStats(highlightedVisits, 'referer')}
highlightedLabel={highlightedLabel}
sortingItems={{ sortingItems={{
name: 'Referrer name', name: 'Referrer name',
amount: 'Visits amount', amount: 'Visits amount',
@ -137,6 +146,7 @@ const VisitsStats = ({ processStatsFromVisits, normalizeVisits }, OpenMapModalBt
title="Countries" title="Countries"
stats={countries} stats={countries}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'country')} highlightedStats={highlightedVisitsToStats(highlightedVisits, 'country')}
highlightedLabel={highlightedLabel}
sortingItems={{ sortingItems={{
name: 'Country name', name: 'Country name',
amount: 'Visits amount', amount: 'Visits amount',
@ -149,6 +159,7 @@ const VisitsStats = ({ processStatsFromVisits, normalizeVisits }, OpenMapModalBt
title="Cities" title="Cities"
stats={cities} stats={cities}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'city')} highlightedStats={highlightedVisitsToStats(highlightedVisits, 'city')}
highlightedLabel={highlightedLabel}
extraHeaderContent={(activeCities) => extraHeaderContent={(activeCities) =>
mapLocations.length > 0 && mapLocations.length > 0 &&
<OpenMapModalBtn modalTitle="Cities" locations={mapLocations} activeCities={activeCities} /> <OpenMapModalBtn modalTitle="Cities" locations={mapLocations} activeCities={activeCities} />

View file

@ -21,6 +21,7 @@ import Checkbox from '../../utils/Checkbox';
const propTypes = { const propTypes = {
title: PropTypes.string, title: PropTypes.string,
highlightedLabel: PropTypes.string,
visits: PropTypes.arrayOf(VisitType), visits: PropTypes.arrayOf(VisitType),
highlightedVisits: PropTypes.arrayOf(VisitType), highlightedVisits: PropTypes.arrayOf(VisitType),
}; };
@ -72,16 +73,14 @@ const generateLabels = (step, visits) => {
]; ];
}; };
const generateLabelsAndGroupedVisits = (visits, step, skipNoElements) => { const generateLabelsAndGroupedVisits = (visits, groupedVisitsWithGaps, step, skipNoElements) => {
const groupedVisits = groupVisitsByStep(step, reverse(visits));
if (skipNoElements) { if (skipNoElements) {
return [ Object.keys(groupedVisits), groupedVisits ]; return [ Object.keys(groupedVisitsWithGaps), groupedVisitsWithGaps ];
} }
const labels = generateLabels(step, visits); const labels = generateLabels(step, visits);
return [ labels, fillTheGaps(groupedVisits, labels) ]; return [ labels, fillTheGaps(groupedVisitsWithGaps, labels) ];
}; };
const generateDataset = (stats, label, color) => ({ const generateDataset = (stats, label, color) => ({
@ -93,12 +92,13 @@ const generateDataset = (stats, label, color) => ({
backgroundColor: color, backgroundColor: color,
}); });
const LineChartCard = ({ title, visits, highlightedVisits }) => { const LineChartCard = ({ title, visits, highlightedVisits, highlightedLabel = 'Selected' }) => {
const [ step, setStep ] = useState('monthly'); const [ step, setStep ] = useState('monthly');
const [ skipNoVisits, toggleSkipNoVisits ] = useToggle(true); const [ skipNoVisits, toggleSkipNoVisits ] = useToggle(true);
const groupedVisitsWithGaps = useMemo(() => groupVisitsByStep(step, reverse(visits)), [ step, visits ]);
const [ labels, groupedVisits ] = useMemo( const [ labels, groupedVisits ] = useMemo(
() => generateLabelsAndGroupedVisits(visits, step, skipNoVisits), () => generateLabelsAndGroupedVisits(visits, groupedVisitsWithGaps, step, skipNoVisits),
[ visits, step, skipNoVisits ] [ visits, step, skipNoVisits ]
); );
const groupedHighlighted = useMemo( const groupedHighlighted = useMemo(
@ -110,7 +110,7 @@ const LineChartCard = ({ title, visits, highlightedVisits }) => {
labels, labels,
datasets: [ datasets: [
generateDataset(groupedVisits, 'Visits', '#4696e5'), generateDataset(groupedVisits, 'Visits', '#4696e5'),
highlightedVisits.length > 0 && generateDataset(groupedHighlighted, 'Selected', '#F77F28'), highlightedVisits.length > 0 && generateDataset(groupedHighlighted, highlightedLabel, '#F77F28'),
].filter(Boolean), ].filter(Boolean),
}; };
const options = { const options = {