@databiosphere/findable-ui 41.1.0 → 41.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +7 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/barX/plot.d.ts +1 -1
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/barX/plot.js +3 -4
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/chart.js +22 -5
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/chart.styles.d.ts +5 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/chart.styles.js +5 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/constants.d.ts +1 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/constants.js +1 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/hook.d.ts +3 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/hook.js +9 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/types.d.ts +4 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/types.js +1 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/utils.d.ts +20 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/utils.js +28 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/hook.d.ts +3 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/hook.js +15 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/types.d.ts +4 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/types.js +1 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/utils.d.ts +15 -0
- package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/utils.js +25 -0
- package/lib/hooks/useCategoryFilter.d.ts +8 -1
- package/lib/hooks/useCategoryFilter.js +1 -1
- package/package.json +1 -1
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/barX/plot.ts +2 -3
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/chart.styles.ts +6 -0
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/chart.tsx +30 -7
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/constants.ts +1 -0
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/hook.ts +18 -0
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/types.ts +4 -0
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UseBarCount/utils.ts +38 -0
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/hook.ts +32 -0
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/types.ts +5 -0
- package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/utils.ts +35 -0
- package/src/hooks/useCategoryFilter.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [41.2.0](https://github.com/DataBiosphere/findable-ui/compare/v41.1.0...v41.2.0) (2025-08-01)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* chart view - show more link for long tail charts ([#446](https://github.com/DataBiosphere/findable-ui/issues/446)) ([#606](https://github.com/DataBiosphere/findable-ui/issues/606)) ([fb035d8](https://github.com/DataBiosphere/findable-ui/commit/fb035d8bbb5ac47ef656ef7403ac271ffdb7e0af))
|
|
9
|
+
|
|
3
10
|
## [41.1.0](https://github.com/DataBiosphere/findable-ui/compare/v41.0.0...v41.1.0) (2025-07-31)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { PlotOptions } from "@observablehq/plot";
|
|
2
2
|
import { SelectCategoryValueView } from "../../../../../../../../../../common/entities";
|
|
3
|
-
export declare function getPlotOptions(selectCategoryValueViews: SelectCategoryValueView[], width: number): PlotOptions;
|
|
3
|
+
export declare function getPlotOptions(selectCategoryValueViews: SelectCategoryValueView[], totalCount: number, width: number): PlotOptions;
|
|
@@ -2,10 +2,9 @@ import * as Plot from "@observablehq/plot";
|
|
|
2
2
|
import { PALETTE } from "../../../../../../../../../../styles/common/constants/palette";
|
|
3
3
|
import { formatCountSize } from "../../../../../../../../../../utils/formatCountSize";
|
|
4
4
|
import { DATA_FIELD, MARGIN_LEFT, TEXT_PADDING } from "./constants";
|
|
5
|
-
import {
|
|
6
|
-
export function getPlotOptions(selectCategoryValueViews, width) {
|
|
5
|
+
import { getCategoryValueText, getCategoryValueTextFill, getColorRangeValue, getCountText, getCountTextFill, getPlotHeight, getTicks, getXDomain, getYPaddingInner, getYPaddingOuter, isAnyValueSelected, renderText, } from "./utils";
|
|
6
|
+
export function getPlotOptions(selectCategoryValueViews, totalCount, width) {
|
|
7
7
|
const isCategorySelected = isAnyValueSelected(selectCategoryValueViews);
|
|
8
|
-
const totalCount = getCategoryTotalCount(selectCategoryValueViews);
|
|
9
8
|
return {
|
|
10
9
|
color: {
|
|
11
10
|
domain: [false, true], // false = unselected, true = selected.
|
|
@@ -34,7 +33,6 @@ export function getPlotOptions(selectCategoryValueViews, width) {
|
|
|
34
33
|
fill: DATA_FIELD.SELECTED,
|
|
35
34
|
rx1: 0,
|
|
36
35
|
rx2: 4,
|
|
37
|
-
sort: { order: "descending", y: "x" }, // Sort by count (x-axis), descending.
|
|
38
36
|
x: DATA_FIELD.COUNT,
|
|
39
37
|
y: DATA_FIELD.LABEL,
|
|
40
38
|
}),
|
|
@@ -79,6 +77,7 @@ export function getPlotOptions(selectCategoryValueViews, width) {
|
|
|
79
77
|
line: false,
|
|
80
78
|
},
|
|
81
79
|
y: {
|
|
80
|
+
domain: selectCategoryValueViews.map((d) => d.label), // Preserve the order of the pre-sorted data.
|
|
82
81
|
label: null,
|
|
83
82
|
line: false,
|
|
84
83
|
paddingInner: getYPaddingInner(),
|
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import {
|
|
3
|
-
import { StyledBarX } from "./chart.styles";
|
|
1
|
+
import React, { Fragment } from "react";
|
|
2
|
+
import { BUTTON_PROPS } from "../../../../../../../../common/Button/constants";
|
|
3
|
+
import { StyledBarX, StyledButton } from "./chart.styles";
|
|
4
|
+
import { useBarCount } from "./hooks/UseBarCount/hook";
|
|
5
|
+
import { isChartExpandable } from "./hooks/UseBarCount/utils";
|
|
6
|
+
import { usePlotOptions } from "./hooks/UsePlotOptions/hook";
|
|
7
|
+
import { renderButtonText } from "./utils";
|
|
4
8
|
export const Chart = ({ selectCategoryValueViews, testId, width, }) => {
|
|
5
|
-
const
|
|
6
|
-
|
|
9
|
+
const { barCount, onToggleBarCount } = useBarCount(selectCategoryValueViews);
|
|
10
|
+
const { options } = usePlotOptions(selectCategoryValueViews, width, barCount);
|
|
11
|
+
return (React.createElement(Fragment, null,
|
|
12
|
+
React.createElement(StyledBarX, { options: options, testId: testId }),
|
|
13
|
+
isChartExpandable(selectCategoryValueViews) && (React.createElement(StyledButton, { ...BUTTON_PROPS.PRIMARY_TEXT, onClick: (e) => {
|
|
14
|
+
// Scroll, only if we are closing the chart (i.e. barCount is undefined).
|
|
15
|
+
const shouldScroll = barCount === undefined;
|
|
16
|
+
onToggleBarCount();
|
|
17
|
+
if (!shouldScroll)
|
|
18
|
+
return;
|
|
19
|
+
// Scroll the chart into view.
|
|
20
|
+
e.currentTarget
|
|
21
|
+
?.closest("div")
|
|
22
|
+
?.scrollIntoView({ behavior: "smooth" });
|
|
23
|
+
} }, renderButtonText(barCount, selectCategoryValueViews)))));
|
|
7
24
|
};
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export declare const StyledBarX: import("@emotion/styled").StyledComponent<import("../../../../../../../../Plot/components/BarX/types").BarXProps & {
|
|
2
2
|
theme?: import("@emotion/react").Theme;
|
|
3
3
|
}, {}, {}>;
|
|
4
|
+
export declare const StyledButton: import("@emotion/styled").StyledComponent<import("@mui/material").ButtonOwnProps & Omit<import("@mui/material").ButtonBaseOwnProps, "classes"> & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
|
|
5
|
+
ref?: ((instance: HTMLButtonElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLButtonElement> | null | undefined;
|
|
6
|
+
}, "style" | "size" | "href" | "className" | "classes" | "children" | "color" | "sx" | "tabIndex" | "disabled" | "action" | "loading" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "disableElevation" | "disableFocusRipple" | "endIcon" | "fullWidth" | "loadingIndicator" | "loadingPosition" | "startIcon" | "variant"> & {
|
|
7
|
+
theme?: import("@emotion/react").Theme;
|
|
8
|
+
}, {}, {}>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import styled from "@emotion/styled";
|
|
2
|
+
import { Button } from "@mui/material";
|
|
2
3
|
import { mediaTabletDown } from "../../../../../../../../../styles/common/mixins/breakpoints";
|
|
3
4
|
import { textBodySmall400 } from "../../../../../../../../../styles/common/mixins/fonts";
|
|
4
5
|
import { BarX } from "../../../../../../../../Plot/components/BarX/barX";
|
|
@@ -27,3 +28,7 @@ export const StyledBarX = styled(BarX) `
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
`;
|
|
31
|
+
export const StyledButton = styled(Button) `
|
|
32
|
+
align-self: flex-start;
|
|
33
|
+
text-transform: none;
|
|
34
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MAX_BAR_COUNT = 20;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MAX_BAR_COUNT = 20;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { initBarCount, updateBarCount } from "./utils";
|
|
3
|
+
export const useBarCount = (selectCategoryValueViews) => {
|
|
4
|
+
const [barCount, setBarCount] = useState(initBarCount(selectCategoryValueViews));
|
|
5
|
+
const onToggleBarCount = useCallback(() => {
|
|
6
|
+
setBarCount(updateBarCount);
|
|
7
|
+
}, []);
|
|
8
|
+
return { barCount, onToggleBarCount };
|
|
9
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SelectCategoryValueView } from "../../../../../../../../../../../common/entities";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the initial bar count for the chart.
|
|
4
|
+
* @param selectCategoryValueViews - Category value views.
|
|
5
|
+
* @returns Initial bar count.
|
|
6
|
+
*/
|
|
7
|
+
export declare function initBarCount(selectCategoryValueViews: SelectCategoryValueView[]): number | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Returns true if the chart is expandable.
|
|
10
|
+
* The chart is expandable if the number of category values is greater than the maximum bar count.
|
|
11
|
+
* @param selectCategoryValueViews - Category value views.
|
|
12
|
+
* @returns True if the chart is expandable.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isChartExpandable(selectCategoryValueViews: SelectCategoryValueView[]): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Updates the bar count for the chart.
|
|
17
|
+
* @param barCount - Current bar count.
|
|
18
|
+
* @returns Updated bar count.
|
|
19
|
+
*/
|
|
20
|
+
export declare function updateBarCount(barCount: number | undefined): number | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { MAX_BAR_COUNT } from "./constants";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the initial bar count for the chart.
|
|
4
|
+
* @param selectCategoryValueViews - Category value views.
|
|
5
|
+
* @returns Initial bar count.
|
|
6
|
+
*/
|
|
7
|
+
export function initBarCount(selectCategoryValueViews) {
|
|
8
|
+
return isChartExpandable(selectCategoryValueViews)
|
|
9
|
+
? MAX_BAR_COUNT
|
|
10
|
+
: undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Returns true if the chart is expandable.
|
|
14
|
+
* The chart is expandable if the number of category values is greater than the maximum bar count.
|
|
15
|
+
* @param selectCategoryValueViews - Category value views.
|
|
16
|
+
* @returns True if the chart is expandable.
|
|
17
|
+
*/
|
|
18
|
+
export function isChartExpandable(selectCategoryValueViews) {
|
|
19
|
+
return selectCategoryValueViews.length > MAX_BAR_COUNT;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Updates the bar count for the chart.
|
|
23
|
+
* @param barCount - Current bar count.
|
|
24
|
+
* @returns Updated bar count.
|
|
25
|
+
*/
|
|
26
|
+
export function updateBarCount(barCount) {
|
|
27
|
+
return barCount === undefined ? MAX_BAR_COUNT : undefined;
|
|
28
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { SelectCategoryValueView } from "../../../../../../../../../../../common/entities";
|
|
2
|
+
import { UsePlotOptions } from "./types";
|
|
3
|
+
export declare const usePlotOptions: (selectCategoryValueViews: SelectCategoryValueView[], width: number, barCount: number | undefined) => UsePlotOptions;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { getPlotOptions } from "../../barX/plot";
|
|
3
|
+
import { getCategoryTotalCount } from "../../barX/utils";
|
|
4
|
+
import { sortByCountThenLabel } from "../../utils";
|
|
5
|
+
export const usePlotOptions = (selectCategoryValueViews, width, barCount) => {
|
|
6
|
+
// Organise the select category value views (sort and slice) for chart display.
|
|
7
|
+
const data = selectCategoryValueViews
|
|
8
|
+
// Sort the category values by count and label.
|
|
9
|
+
.sort(sortByCountThenLabel)
|
|
10
|
+
// Slice the category values to the number of bars to display.
|
|
11
|
+
.slice(0, barCount);
|
|
12
|
+
// Build the plot options.
|
|
13
|
+
const options = useMemo(() => getPlotOptions(data, getCategoryTotalCount(selectCategoryValueViews), width), [data, selectCategoryValueViews, width]);
|
|
14
|
+
return { options };
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SelectCategoryValueView } from "../../../../../../../../../common/entities";
|
|
2
|
+
/**
|
|
3
|
+
* Renders the button text for the chart.
|
|
4
|
+
* @param maxBarCount - Maximum number of bars to display.
|
|
5
|
+
* @param selectCategoryValueViews - Category value views.
|
|
6
|
+
* @returns Button text.
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderButtonText(maxBarCount: number | undefined, selectCategoryValueViews: SelectCategoryValueView[]): string;
|
|
9
|
+
/**
|
|
10
|
+
* Sorts category value views by count in descending order, then label in ascending order.
|
|
11
|
+
* @param a - First category value view.
|
|
12
|
+
* @param b - Second category value view.
|
|
13
|
+
* @returns Sorted category value views.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sortByCountThenLabel(a: SelectCategoryValueView, b: SelectCategoryValueView): number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { sortCategoryValueViews } from "../../../../../../../../../hooks/useCategoryFilter";
|
|
2
|
+
/**
|
|
3
|
+
* Renders the button text for the chart.
|
|
4
|
+
* @param maxBarCount - Maximum number of bars to display.
|
|
5
|
+
* @param selectCategoryValueViews - Category value views.
|
|
6
|
+
* @returns Button text.
|
|
7
|
+
*/
|
|
8
|
+
export function renderButtonText(maxBarCount, selectCategoryValueViews) {
|
|
9
|
+
if (!maxBarCount)
|
|
10
|
+
return "Show less";
|
|
11
|
+
// Calculate the number of additional bars to show.
|
|
12
|
+
const totalBars = selectCategoryValueViews.length;
|
|
13
|
+
const count = totalBars - maxBarCount;
|
|
14
|
+
return `Show ${count} additional results`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sorts category value views by count in descending order, then label in ascending order.
|
|
18
|
+
* @param a - First category value view.
|
|
19
|
+
* @param b - Second category value view.
|
|
20
|
+
* @returns Sorted category value views.
|
|
21
|
+
*/
|
|
22
|
+
export function sortByCountThenLabel(a, b) {
|
|
23
|
+
const compare = b.count - a.count;
|
|
24
|
+
return compare === 0 ? sortCategoryValueViews(a, b) : compare;
|
|
25
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CategoryConfig } from "../common/categories/config/types";
|
|
2
2
|
import { Category } from "../common/categories/models/types";
|
|
3
3
|
import { CategoryView, VIEW_KIND } from "../common/categories/views/types";
|
|
4
|
-
import { CategoryKey, CategoryValueKey, ClearAll, Filters, SelectCategoryValue } from "../common/entities";
|
|
4
|
+
import { CategoryKey, CategoryValueKey, ClearAll, Filters, SelectCategoryValue, SelectCategoryValueView } from "../common/entities";
|
|
5
5
|
/**
|
|
6
6
|
* State backing filter functionality and calculations. Converted to view model for display.
|
|
7
7
|
*/
|
|
@@ -34,3 +34,10 @@ export declare function buildNextFilterState(filterState: FilterState, categoryK
|
|
|
34
34
|
* @returns original select category value.
|
|
35
35
|
*/
|
|
36
36
|
export declare function getSelectCategoryValue(selectCategoryValue: SelectCategoryValue): SelectCategoryValue;
|
|
37
|
+
/**
|
|
38
|
+
* Sort category value views by key, ascending.
|
|
39
|
+
* @param cvv0 - First category value view to compare.
|
|
40
|
+
* @param cvv1 - Second category value view to compare.
|
|
41
|
+
* @returns Number indicating sort precedence of cv0 vs cv1.
|
|
42
|
+
*/
|
|
43
|
+
export declare function sortCategoryValueViews(cvv0: SelectCategoryValueView, cvv1: SelectCategoryValueView): number;
|
|
@@ -162,7 +162,7 @@ function isCategoryAcceptListed(category, categoryConfigs) {
|
|
|
162
162
|
* @param cvv1 - Second category value view to compare.
|
|
163
163
|
* @returns Number indicating sort precedence of cv0 vs cv1.
|
|
164
164
|
*/
|
|
165
|
-
function sortCategoryValueViews(cvv0, cvv1) {
|
|
165
|
+
export function sortCategoryValueViews(cvv0, cvv1) {
|
|
166
166
|
return !cvv0.label
|
|
167
167
|
? 1
|
|
168
168
|
: !cvv1.label
|
package/package.json
CHANGED
|
@@ -5,7 +5,6 @@ import { PALETTE } from "../../../../../../../../../../styles/common/constants/p
|
|
|
5
5
|
import { formatCountSize } from "../../../../../../../../../../utils/formatCountSize";
|
|
6
6
|
import { DATA_FIELD, MARGIN_LEFT, TEXT_PADDING } from "./constants";
|
|
7
7
|
import {
|
|
8
|
-
getCategoryTotalCount,
|
|
9
8
|
getCategoryValueText,
|
|
10
9
|
getCategoryValueTextFill,
|
|
11
10
|
getColorRangeValue,
|
|
@@ -22,10 +21,10 @@ import {
|
|
|
22
21
|
|
|
23
22
|
export function getPlotOptions(
|
|
24
23
|
selectCategoryValueViews: SelectCategoryValueView[],
|
|
24
|
+
totalCount: number,
|
|
25
25
|
width: number
|
|
26
26
|
): PlotOptions {
|
|
27
27
|
const isCategorySelected = isAnyValueSelected(selectCategoryValueViews);
|
|
28
|
-
const totalCount = getCategoryTotalCount(selectCategoryValueViews);
|
|
29
28
|
return {
|
|
30
29
|
color: {
|
|
31
30
|
domain: [false, true], // false = unselected, true = selected.
|
|
@@ -54,7 +53,6 @@ export function getPlotOptions(
|
|
|
54
53
|
fill: DATA_FIELD.SELECTED,
|
|
55
54
|
rx1: 0,
|
|
56
55
|
rx2: 4,
|
|
57
|
-
sort: { order: "descending", y: "x" }, // Sort by count (x-axis), descending.
|
|
58
56
|
x: DATA_FIELD.COUNT,
|
|
59
57
|
y: DATA_FIELD.LABEL,
|
|
60
58
|
}),
|
|
@@ -99,6 +97,7 @@ export function getPlotOptions(
|
|
|
99
97
|
line: false,
|
|
100
98
|
},
|
|
101
99
|
y: {
|
|
100
|
+
domain: selectCategoryValueViews.map((d) => d.label), // Preserve the order of the pre-sorted data.
|
|
102
101
|
label: null,
|
|
103
102
|
line: false,
|
|
104
103
|
paddingInner: getYPaddingInner(),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import styled from "@emotion/styled";
|
|
2
|
+
import { Button } from "@mui/material";
|
|
2
3
|
import { mediaTabletDown } from "../../../../../../../../../styles/common/mixins/breakpoints";
|
|
3
4
|
import { textBodySmall400 } from "../../../../../../../../../styles/common/mixins/fonts";
|
|
4
5
|
import { BarX } from "../../../../../../../../Plot/components/BarX/barX";
|
|
@@ -28,3 +29,8 @@ export const StyledBarX = styled(BarX)`
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
`;
|
|
32
|
+
|
|
33
|
+
export const StyledButton = styled(Button)`
|
|
34
|
+
align-self: flex-start;
|
|
35
|
+
text-transform: none;
|
|
36
|
+
`;
|
|
@@ -1,16 +1,39 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import {
|
|
3
|
-
import { StyledBarX } from "./chart.styles";
|
|
1
|
+
import React, { Fragment } from "react";
|
|
2
|
+
import { BUTTON_PROPS } from "../../../../../../../../common/Button/constants";
|
|
3
|
+
import { StyledBarX, StyledButton } from "./chart.styles";
|
|
4
|
+
import { useBarCount } from "./hooks/UseBarCount/hook";
|
|
5
|
+
import { isChartExpandable } from "./hooks/UseBarCount/utils";
|
|
6
|
+
import { usePlotOptions } from "./hooks/UsePlotOptions/hook";
|
|
4
7
|
import { ChartProps } from "./types";
|
|
8
|
+
import { renderButtonText } from "./utils";
|
|
5
9
|
|
|
6
10
|
export const Chart = ({
|
|
7
11
|
selectCategoryValueViews,
|
|
8
12
|
testId,
|
|
9
13
|
width,
|
|
10
14
|
}: ChartProps): JSX.Element => {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
const { barCount, onToggleBarCount } = useBarCount(selectCategoryValueViews);
|
|
16
|
+
const { options } = usePlotOptions(selectCategoryValueViews, width, barCount);
|
|
17
|
+
return (
|
|
18
|
+
<Fragment>
|
|
19
|
+
<StyledBarX options={options} testId={testId} />
|
|
20
|
+
{isChartExpandable(selectCategoryValueViews) && (
|
|
21
|
+
<StyledButton
|
|
22
|
+
{...BUTTON_PROPS.PRIMARY_TEXT}
|
|
23
|
+
onClick={(e) => {
|
|
24
|
+
// Scroll, only if we are closing the chart (i.e. barCount is undefined).
|
|
25
|
+
const shouldScroll = barCount === undefined;
|
|
26
|
+
onToggleBarCount();
|
|
27
|
+
if (!shouldScroll) return;
|
|
28
|
+
// Scroll the chart into view.
|
|
29
|
+
e.currentTarget
|
|
30
|
+
?.closest("div")
|
|
31
|
+
?.scrollIntoView({ behavior: "smooth" });
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
{renderButtonText(barCount, selectCategoryValueViews)}
|
|
35
|
+
</StyledButton>
|
|
36
|
+
)}
|
|
37
|
+
</Fragment>
|
|
14
38
|
);
|
|
15
|
-
return <StyledBarX options={options} testId={testId} />;
|
|
16
39
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MAX_BAR_COUNT = 20;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { SelectCategoryValueView } from "../../../../../../../../../../../common/entities";
|
|
3
|
+
import { UseBarCount } from "./types";
|
|
4
|
+
import { initBarCount, updateBarCount } from "./utils";
|
|
5
|
+
|
|
6
|
+
export const useBarCount = (
|
|
7
|
+
selectCategoryValueViews: SelectCategoryValueView[]
|
|
8
|
+
): UseBarCount => {
|
|
9
|
+
const [barCount, setBarCount] = useState<number | undefined>(
|
|
10
|
+
initBarCount(selectCategoryValueViews)
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const onToggleBarCount = useCallback(() => {
|
|
14
|
+
setBarCount(updateBarCount);
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
return { barCount, onToggleBarCount };
|
|
18
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SelectCategoryValueView } from "../../../../../../../../../../../common/entities";
|
|
2
|
+
import { MAX_BAR_COUNT } from "./constants";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the initial bar count for the chart.
|
|
6
|
+
* @param selectCategoryValueViews - Category value views.
|
|
7
|
+
* @returns Initial bar count.
|
|
8
|
+
*/
|
|
9
|
+
export function initBarCount(
|
|
10
|
+
selectCategoryValueViews: SelectCategoryValueView[]
|
|
11
|
+
): number | undefined {
|
|
12
|
+
return isChartExpandable(selectCategoryValueViews)
|
|
13
|
+
? MAX_BAR_COUNT
|
|
14
|
+
: undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns true if the chart is expandable.
|
|
19
|
+
* The chart is expandable if the number of category values is greater than the maximum bar count.
|
|
20
|
+
* @param selectCategoryValueViews - Category value views.
|
|
21
|
+
* @returns True if the chart is expandable.
|
|
22
|
+
*/
|
|
23
|
+
export function isChartExpandable(
|
|
24
|
+
selectCategoryValueViews: SelectCategoryValueView[]
|
|
25
|
+
): boolean {
|
|
26
|
+
return selectCategoryValueViews.length > MAX_BAR_COUNT;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Updates the bar count for the chart.
|
|
31
|
+
* @param barCount - Current bar count.
|
|
32
|
+
* @returns Updated bar count.
|
|
33
|
+
*/
|
|
34
|
+
export function updateBarCount(
|
|
35
|
+
barCount: number | undefined
|
|
36
|
+
): number | undefined {
|
|
37
|
+
return barCount === undefined ? MAX_BAR_COUNT : undefined;
|
|
38
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { SelectCategoryValueView } from "../../../../../../../../../../../common/entities";
|
|
3
|
+
import { getPlotOptions } from "../../barX/plot";
|
|
4
|
+
import { getCategoryTotalCount } from "../../barX/utils";
|
|
5
|
+
import { sortByCountThenLabel } from "../../utils";
|
|
6
|
+
import { UsePlotOptions } from "./types";
|
|
7
|
+
|
|
8
|
+
export const usePlotOptions = (
|
|
9
|
+
selectCategoryValueViews: SelectCategoryValueView[],
|
|
10
|
+
width: number,
|
|
11
|
+
barCount: number | undefined
|
|
12
|
+
): UsePlotOptions => {
|
|
13
|
+
// Organise the select category value views (sort and slice) for chart display.
|
|
14
|
+
const data = selectCategoryValueViews
|
|
15
|
+
// Sort the category values by count and label.
|
|
16
|
+
.sort(sortByCountThenLabel)
|
|
17
|
+
// Slice the category values to the number of bars to display.
|
|
18
|
+
.slice(0, barCount);
|
|
19
|
+
|
|
20
|
+
// Build the plot options.
|
|
21
|
+
const options = useMemo(
|
|
22
|
+
() =>
|
|
23
|
+
getPlotOptions(
|
|
24
|
+
data,
|
|
25
|
+
getCategoryTotalCount(selectCategoryValueViews),
|
|
26
|
+
width
|
|
27
|
+
),
|
|
28
|
+
[data, selectCategoryValueViews, width]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return { options };
|
|
32
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SelectCategoryValueView } from "../../../../../../../../../common/entities";
|
|
2
|
+
import { sortCategoryValueViews } from "../../../../../../../../../hooks/useCategoryFilter";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders the button text for the chart.
|
|
6
|
+
* @param maxBarCount - Maximum number of bars to display.
|
|
7
|
+
* @param selectCategoryValueViews - Category value views.
|
|
8
|
+
* @returns Button text.
|
|
9
|
+
*/
|
|
10
|
+
export function renderButtonText(
|
|
11
|
+
maxBarCount: number | undefined,
|
|
12
|
+
selectCategoryValueViews: SelectCategoryValueView[]
|
|
13
|
+
): string {
|
|
14
|
+
if (!maxBarCount) return "Show less";
|
|
15
|
+
|
|
16
|
+
// Calculate the number of additional bars to show.
|
|
17
|
+
const totalBars = selectCategoryValueViews.length;
|
|
18
|
+
const count = totalBars - maxBarCount;
|
|
19
|
+
|
|
20
|
+
return `Show ${count} additional results`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Sorts category value views by count in descending order, then label in ascending order.
|
|
25
|
+
* @param a - First category value view.
|
|
26
|
+
* @param b - Second category value view.
|
|
27
|
+
* @returns Sorted category value views.
|
|
28
|
+
*/
|
|
29
|
+
export function sortByCountThenLabel(
|
|
30
|
+
a: SelectCategoryValueView,
|
|
31
|
+
b: SelectCategoryValueView
|
|
32
|
+
): number {
|
|
33
|
+
const compare = b.count - a.count;
|
|
34
|
+
return compare === 0 ? sortCategoryValueViews(a, b) : compare;
|
|
35
|
+
}
|
|
@@ -278,7 +278,7 @@ function isCategoryAcceptListed(
|
|
|
278
278
|
* @param cvv1 - Second category value view to compare.
|
|
279
279
|
* @returns Number indicating sort precedence of cv0 vs cv1.
|
|
280
280
|
*/
|
|
281
|
-
function sortCategoryValueViews(
|
|
281
|
+
export function sortCategoryValueViews(
|
|
282
282
|
cvv0: SelectCategoryValueView,
|
|
283
283
|
cvv1: SelectCategoryValueView
|
|
284
284
|
): number {
|