synapse-react-client 4.0.23 → 4.0.25
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/dist/SWC.index.d.ts +1 -0
- package/dist/SWC.index.d.ts.map +1 -1
- package/dist/SWC.index.js +2 -1
- package/dist/SWC.index.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsFilesForm/DataAccessRequestAccessorsFilesForm.js.map +1 -1
- package/dist/components/AclEditor/useUpdateAcl.js.map +1 -1
- package/dist/components/Authentication/TwoFactorBackupCodes.js.map +1 -1
- package/dist/components/Authentication/TwoFactorEnrollmentForm.js.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +3 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.js +43 -30
- package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/components/CannedRejectionDialog/CannedRejectionDialog.js.map +1 -1
- package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js.map +1 -1
- package/dist/components/CopyToClipboardString/CopyToClipboardString.d.ts +7 -1
- package/dist/components/CopyToClipboardString/CopyToClipboardString.d.ts.map +1 -1
- package/dist/components/CopyToClipboardString/CopyToClipboardString.js +22 -16
- package/dist/components/CopyToClipboardString/CopyToClipboardString.js.map +1 -1
- package/dist/components/EntityCitation/EntityCitation.d.ts.map +1 -1
- package/dist/components/EntityCitation/EntityCitation.js +28 -26
- package/dist/components/EntityCitation/EntityCitation.js.map +1 -1
- package/dist/components/EntityFinder/tree/EntityTree.js.map +1 -1
- package/dist/components/EntityFinder/tree/VirtualizedTree.js.map +1 -1
- package/dist/components/EntityIcon.d.ts +1 -0
- package/dist/components/EntityIcon.d.ts.map +1 -1
- package/dist/components/EntityIcon.js +37 -36
- package/dist/components/EntityIcon.js.map +1 -1
- package/dist/components/EntitySidebar/EntitySidebar.css +1 -0
- package/dist/components/EntitySidebar/EntitySidebar.d.ts +7 -0
- package/dist/components/EntitySidebar/EntitySidebar.d.ts.map +1 -0
- package/dist/components/EntitySidebar/EntitySidebar.js +62 -0
- package/dist/components/EntitySidebar/EntitySidebar.js.map +1 -0
- package/dist/components/EntitySidebar/EntitySidebar.module.js +17 -0
- package/dist/components/EntitySidebar/EntitySidebar.module.js.map +1 -0
- package/dist/components/EntitySidebar/EntitySidebar.module.scss +87 -0
- package/dist/components/Evaluation/EvaluationRoundEditorList.js.map +1 -1
- package/dist/components/GenericCard/BioregistryRules.js.map +1 -1
- package/dist/components/IconSvg/IconSvg.d.ts +2 -1
- package/dist/components/IconSvg/IconSvg.d.ts.map +1 -1
- package/dist/components/IconSvg/IconSvg.js +112 -89
- package/dist/components/IconSvg/IconSvg.js.map +1 -1
- package/dist/components/Plot/SynapsePlot.d.ts +4 -0
- package/dist/components/Plot/SynapsePlot.d.ts.map +1 -1
- package/dist/components/Plot/SynapsePlot.js +57 -44
- package/dist/components/Plot/SynapsePlot.js.map +1 -1
- package/dist/components/Plot/UpsetPlot.js.map +1 -1
- package/dist/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.d.ts.map +1 -1
- package/dist/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.js +77 -34
- package/dist/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.js.map +1 -1
- package/dist/components/SchemaDrivenAnnotationEditor/field/AdditionalPropertiesSchemaField.js.map +1 -1
- package/dist/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.d.ts.map +1 -1
- package/dist/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.js +1 -0
- package/dist/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.js.map +1 -1
- package/dist/components/StorybookComponentWrapper.js.map +1 -1
- package/dist/components/SynapseTable/TablePagination.js.map +1 -1
- package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js.map +1 -1
- package/dist/components/TanStackTable/StyledTanStackTable.js.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.d.ts +2 -0
- package/dist/components/entity/page/action_menu/EntityActionMenu.d.ts.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.js +46 -27
- package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
- package/dist/components/entity/page/breadcrumbs/EntityPageBreadcrumbs.d.ts +1 -8
- package/dist/components/entity/page/breadcrumbs/EntityPageBreadcrumbs.d.ts.map +1 -1
- package/dist/components/entity/page/breadcrumbs/EntityPageBreadcrumbs.js +17 -13
- package/dist/components/entity/page/breadcrumbs/EntityPageBreadcrumbs.js.map +1 -1
- package/dist/components/entity/page/title_bar/EntityPageTitleBar.d.ts +1 -0
- package/dist/components/entity/page/title_bar/EntityPageTitleBar.d.ts.map +1 -1
- package/dist/components/entity/page/title_bar/EntityPageTitleBar.js +35 -22
- package/dist/components/entity/page/title_bar/EntityPageTitleBar.js.map +1 -1
- package/dist/components/entity/page/title_bar/EntityTitleBarVersionInfo.d.ts.map +1 -1
- package/dist/components/entity/page/title_bar/EntityTitleBarVersionInfo.js +34 -20
- package/dist/components/entity/page/title_bar/EntityTitleBarVersionInfo.js.map +1 -1
- package/dist/components/entity/page/title_bar/TitleBarProperties.d.ts +0 -1
- package/dist/components/entity/page/title_bar/TitleBarProperties.d.ts.map +1 -1
- package/dist/components/entity/page/title_bar/TitleBarProperties.js +17 -82
- package/dist/components/entity/page/title_bar/TitleBarProperties.js.map +1 -1
- package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.d.ts.map +1 -1
- package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.js +3 -4
- package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.js.map +1 -1
- package/dist/components/favorites/FavoriteButton.d.ts +1 -0
- package/dist/components/favorites/FavoriteButton.d.ts.map +1 -1
- package/dist/components/favorites/FavoriteButton.js +8 -8
- package/dist/components/favorites/FavoriteButton.js.map +1 -1
- package/dist/components/menu/ComplexMenu.d.ts.map +1 -1
- package/dist/components/menu/ComplexMenu.js +5 -3
- package/dist/components/menu/ComplexMenu.js.map +1 -1
- package/dist/components/menu/DropdownMenu.d.ts +1 -0
- package/dist/components/menu/DropdownMenu.d.ts.map +1 -1
- package/dist/components/menu/DropdownMenu.js +24 -24
- package/dist/components/menu/DropdownMenu.js.map +1 -1
- package/dist/components/widgets/facet-nav/FacetNavPanel.d.ts.map +1 -1
- package/dist/components/widgets/facet-nav/FacetNavPanel.js +59 -49
- package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
- package/dist/components/widgets/facet-nav/PlotsContainer.d.ts.map +1 -1
- package/dist/components/widgets/facet-nav/PlotsContainer.js +8 -2
- package/dist/components/widgets/facet-nav/PlotsContainer.js.map +1 -1
- package/dist/style/components/facet_nav/_facet-nav-panel.scss +11 -0
- package/dist/style/main.css +1 -1
- package/dist/testutils/ReactQueryMockUtils.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/ThemeTypes.d.ts +2 -2
- package/dist/utils/functions/IsEqualTreatUndefinedAsMissing.js.map +1 -1
- package/dist/utils/hooks/useDebouncedEffect.js.map +1 -1
- package/dist/utils/hooks/useDetectSSOCode.js.map +1 -1
- package/dist/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.js.map +1 -1
- package/dist/utils/hooks/useLogin.js.map +1 -1
- package/package.json +3 -3
|
@@ -3,71 +3,84 @@ import { parseEntityIdFromSqlStatement as e } from "../../utils/functions/SqlFun
|
|
|
3
3
|
import "../../utils/index.js";
|
|
4
4
|
import { useGetFullTableQueryResults as t } from "../../synapse-queries/entity/useGetQueryResultBundle.js";
|
|
5
5
|
import "../../synapse-queries/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { unCamelCase as n } from "../../utils/functions/unCamelCase.js";
|
|
7
|
+
import { Plot as r } from "./Plot.js";
|
|
7
8
|
import './SynapsePlot.css';/* empty css */
|
|
8
9
|
import "react";
|
|
9
|
-
import { Skeleton as
|
|
10
|
-
import { Fragment as
|
|
10
|
+
import { Skeleton as i } from "@mui/material";
|
|
11
|
+
import { Fragment as a, jsx as o } from "react/jsx-runtime";
|
|
11
12
|
//#region src/components/Plot/SynapsePlot.tsx
|
|
12
|
-
var
|
|
13
|
-
let { query:
|
|
13
|
+
var s = (s) => {
|
|
14
|
+
let { query: c } = s.synapsePlotWidgetParams, { selectedFacets: l, additionalFilters: u, onCustomPlotClick: d, queryContext: f } = s.customPlotParams ?? {}, { data: p, isLoading: m } = t({
|
|
14
15
|
concreteType: "org.sagebionetworks.repo.model.table.QueryBundleRequest",
|
|
15
16
|
partMask: 1,
|
|
16
|
-
entityId: e(
|
|
17
|
+
entityId: e(c),
|
|
17
18
|
query: {
|
|
18
|
-
sql:
|
|
19
|
-
selectedFacets:
|
|
20
|
-
additionalFilters:
|
|
19
|
+
sql: c,
|
|
20
|
+
selectedFacets: l,
|
|
21
|
+
additionalFilters: u
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
|
-
if (
|
|
24
|
+
if (m) return /* @__PURE__ */ o(i, {
|
|
24
25
|
width: "100%",
|
|
25
26
|
height: "200px"
|
|
26
27
|
});
|
|
27
|
-
if (!
|
|
28
|
-
let { title:
|
|
29
|
-
showlegend:
|
|
30
|
-
title:
|
|
31
|
-
barmode:
|
|
28
|
+
if (!p) return /* @__PURE__ */ o(a, {});
|
|
29
|
+
let { title: h, xtitle: g, ytitle: _, type: v, xaxistype: y, showlegend: b, barmode: x, horizontal: S, displayModeBar: C, hoverinfo: w, hideYAxisTickLabels: T, hideXAxisTickLabels: E, footnote: D } = s.synapsePlotWidgetParams, O = { displayModeBar: C }, k = {
|
|
30
|
+
showlegend: b,
|
|
31
|
+
title: h,
|
|
32
|
+
barmode: x,
|
|
33
|
+
...D && { margin: { b: 40 } }
|
|
32
34
|
};
|
|
33
|
-
|
|
34
|
-
...
|
|
35
|
-
|
|
36
|
-
}),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
35
|
+
(g || E) && (k.xaxis = {
|
|
36
|
+
...g && { title: g },
|
|
37
|
+
...E && { showticklabels: !1 }
|
|
38
|
+
}), y && (k.xaxis = {
|
|
39
|
+
...k.xaxis,
|
|
40
|
+
type: y
|
|
41
|
+
}), (_ || T) && (k.yaxis = {
|
|
42
|
+
..._ && { title: _ },
|
|
43
|
+
...T && { showticklabels: !1 }
|
|
44
|
+
});
|
|
45
|
+
let A = [], j = S ? "h" : "v", M = p.queryResult?.queryResults.headers ?? [], N = p.queryResult?.queryResults.rows ?? [];
|
|
46
|
+
if (M.length == 0 || N.length == 0) return /* @__PURE__ */ o(a, {});
|
|
47
|
+
for (let e = 0; e < M.length - 1; e += 1) {
|
|
48
|
+
let t = M[e + 1].name, r = n(t.replace(/_/g, " ")), i = v === "bar" ? S ? `<b>%{y}</b><br>${r}: %{x:,}<extra></extra>` : `<b>%{x}</b><br>${r}: %{y:,}<extra></extra>` : void 0;
|
|
49
|
+
A[e] = {
|
|
50
|
+
orientation: j,
|
|
51
|
+
name: t,
|
|
52
|
+
type: v,
|
|
53
|
+
x: [],
|
|
54
|
+
y: [],
|
|
55
|
+
customdata: [],
|
|
56
|
+
...i !== void 0 && { hovertemplate: i },
|
|
57
|
+
...w !== void 0 && { hoverinfo: w }
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
for (let e of N) for (let t = 1; t < e.values.length; t += 1) {
|
|
61
|
+
let n = e.values, r = A[t - 1].x, i = A[t - 1].y, a = A[t - 1].customdata;
|
|
62
|
+
r.push(S ? n[t] : n[0]), i.push(S ? n[0] : n[t]), a.push(JSON.stringify(e));
|
|
51
63
|
}
|
|
52
|
-
let
|
|
53
|
-
return
|
|
54
|
-
|
|
64
|
+
let P;
|
|
65
|
+
return d && (P = (e) => {
|
|
66
|
+
d({
|
|
55
67
|
row: JSON.parse(e.points[0].customdata),
|
|
56
|
-
queryContext:
|
|
68
|
+
queryContext: f
|
|
57
69
|
});
|
|
58
|
-
}), /* @__PURE__ */
|
|
70
|
+
}), /* @__PURE__ */ o(r, {
|
|
59
71
|
style: {
|
|
60
72
|
width: "100%",
|
|
61
73
|
height: "450px"
|
|
62
74
|
},
|
|
63
|
-
className:
|
|
64
|
-
layout:
|
|
65
|
-
data:
|
|
66
|
-
config:
|
|
67
|
-
onClick:
|
|
75
|
+
className: P ? "SynapsePlot--clickable" : void 0,
|
|
76
|
+
layout: k,
|
|
77
|
+
data: A,
|
|
78
|
+
config: O,
|
|
79
|
+
onClick: P,
|
|
80
|
+
useResizeHandler: !0
|
|
68
81
|
});
|
|
69
82
|
};
|
|
70
83
|
//#endregion
|
|
71
|
-
export {
|
|
84
|
+
export { s as SynapsePlot, s as default };
|
|
72
85
|
|
|
73
86
|
//# sourceMappingURL=SynapsePlot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SynapsePlot.js","names":[],"sources":["../../../src/components/Plot/SynapsePlot.tsx"],"sourcesContent":["import React from 'react'\nimport { useGetFullTableQueryResults } from '@/synapse-queries'\nimport { SynapseConstants } from '@/utils'\nimport { parseEntityIdFromSqlStatement } from '@/utils/functions/SqlFunctions'\nimport { Skeleton } from '@mui/material'\nimport {\n FacetColumnRequest,\n QueryBundleRequest,\n QueryFilter,\n Row,\n} from '@sage-bionetworks/synapse-types'\nimport Plotly, { AxisType, PlotType } from 'plotly.js-basic-dist'\nimport { QueryContextType } from '../QueryContext'\nimport { QueryWrapperSynapsePlotRowClickEvent } from '../QueryWrapperPlotNav/QueryWrapperSynapsePlot'\nimport Plot from './Plot'\nimport './SynapsePlot.scss'\n\nexport type SynapsePlotWidgetParams = {\n query: string //sql string\n title?: string //plot title\n xtitle?: Plotly.LayoutAxis['title'] // x-axis title\n ytitle?: Plotly.LayoutAxis['title'] // y-axis title\n type: PlotType\n xaxistype?: AxisType\n showlegend?: Plotly.Layout['showlegend'] // sets the legend visibility\n horizontal?: boolean // sets the if a bar chart should be horizontal or vertical\n barmode?: Plotly.Layout['barmode'] // Plotly barmode\n displayModeBar?: Plotly.Config['displayModeBar'] // sets the modebar visibility\n hoverinfo?: Plotly.PlotData['hoverinfo'] // sets the hover info\n}\n\n// QueryWrapperPlotNav customPlot parameters, undefined otherwise\nexport type QueryWrapperPlotNavCustomPlotParams = {\n selectedFacets: FacetColumnRequest[]\n additionalFilters: QueryFilter[]\n onCustomPlotClick?: (event: QueryWrapperSynapsePlotRowClickEvent) => void\n queryContext?: QueryContextType\n}\nexport type SynapsePlotProps = {\n synapsePlotWidgetParams: SynapsePlotWidgetParams\n customPlotParams?: QueryWrapperPlotNavCustomPlotParams\n}\n\nexport const SynapsePlot = (props: SynapsePlotProps): React.ReactNode => {\n const { query } = props.synapsePlotWidgetParams\n const { selectedFacets, additionalFilters, onCustomPlotClick, queryContext } =\n props.customPlotParams ?? {}\n const queryRequest: QueryBundleRequest = {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n partMask: SynapseConstants.BUNDLE_MASK_QUERY_RESULTS,\n entityId: parseEntityIdFromSqlStatement(query),\n query: {\n sql: query,\n selectedFacets,\n additionalFilters,\n },\n }\n const { data: queryData, isLoading } =\n useGetFullTableQueryResults(queryRequest)\n if (isLoading) {\n return <Skeleton width={'100%'} height={'200px'} />\n }\n if (!queryData) {\n return <></>\n }\n\n const {\n title,\n xtitle,\n ytitle,\n type,\n xaxistype,\n showlegend,\n barmode,\n horizontal,\n displayModeBar,\n hoverinfo,\n } = props.synapsePlotWidgetParams\n\n const config: Partial<Plotly.Config> = {\n displayModeBar,\n }\n const layout: Partial<Plotly.Layout> = {\n showlegend: showlegend,\n title,\n barmode: barmode,\n }\n if (xtitle) {\n layout.xaxis = {\n title: xtitle,\n }\n }\n if (xaxistype) {\n layout.xaxis = {\n ...layout.xaxis,\n type: xaxistype,\n }\n }\n if (ytitle) {\n layout.yaxis = {\n title: ytitle,\n }\n }\n // init plot_data\n const plotData: Partial<Plotly.PlotData>[] = []\n const orientation = horizontal ? 'h' : 'v'\n const headers = queryData.queryResult?.queryResults.headers ?? []\n const rows = queryData.queryResult?.queryResults.rows ?? []\n if (headers.length == 0 || rows.length == 0) {\n return <></>\n }\n for (let i = 0; i < headers.length - 1; i += 1) {\n // make an entry for each set of data points\n plotData[i] = {\n orientation,\n name:
|
|
1
|
+
{"version":3,"file":"SynapsePlot.js","names":[],"sources":["../../../src/components/Plot/SynapsePlot.tsx"],"sourcesContent":["import React from 'react'\nimport { useGetFullTableQueryResults } from '@/synapse-queries'\nimport { SynapseConstants } from '@/utils'\nimport { parseEntityIdFromSqlStatement } from '@/utils/functions/SqlFunctions'\nimport { Skeleton } from '@mui/material'\nimport {\n FacetColumnRequest,\n QueryBundleRequest,\n QueryFilter,\n Row,\n} from '@sage-bionetworks/synapse-types'\nimport Plotly, { AxisType, PlotType } from 'plotly.js-basic-dist'\nimport { QueryContextType } from '../QueryContext'\nimport { QueryWrapperSynapsePlotRowClickEvent } from '../QueryWrapperPlotNav/QueryWrapperSynapsePlot'\nimport Plot from './Plot'\nimport { unCamelCase } from '@/utils/functions/unCamelCase'\nimport './SynapsePlot.scss'\n\nexport type SynapsePlotWidgetParams = {\n query: string //sql string\n title?: string //plot title\n xtitle?: Plotly.LayoutAxis['title'] // x-axis title\n ytitle?: Plotly.LayoutAxis['title'] // y-axis title\n type: PlotType\n xaxistype?: AxisType\n showlegend?: Plotly.Layout['showlegend'] // sets the legend visibility\n horizontal?: boolean // sets the if a bar chart should be horizontal or vertical\n barmode?: Plotly.Layout['barmode'] // Plotly barmode\n displayModeBar?: Plotly.Config['displayModeBar'] // sets the modebar visibility\n hoverinfo?: Plotly.PlotData['hoverinfo'] // sets the hover info\n hideYAxisTickLabels?: boolean // hides the y-axis tick labels\n hideXAxisTickLabels?: boolean // hides the x-axis tick labels\n footnote?: string // optional text displayed below the plot\n fullWidth?: boolean // forces the plot to take up the full container width\n}\n\n// QueryWrapperPlotNav customPlot parameters, undefined otherwise\nexport type QueryWrapperPlotNavCustomPlotParams = {\n selectedFacets: FacetColumnRequest[]\n additionalFilters: QueryFilter[]\n onCustomPlotClick?: (event: QueryWrapperSynapsePlotRowClickEvent) => void\n queryContext?: QueryContextType\n}\nexport type SynapsePlotProps = {\n synapsePlotWidgetParams: SynapsePlotWidgetParams\n customPlotParams?: QueryWrapperPlotNavCustomPlotParams\n}\n\nexport const SynapsePlot = (props: SynapsePlotProps): React.ReactNode => {\n const { query } = props.synapsePlotWidgetParams\n const { selectedFacets, additionalFilters, onCustomPlotClick, queryContext } =\n props.customPlotParams ?? {}\n const queryRequest: QueryBundleRequest = {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n partMask: SynapseConstants.BUNDLE_MASK_QUERY_RESULTS,\n entityId: parseEntityIdFromSqlStatement(query),\n query: {\n sql: query,\n selectedFacets,\n additionalFilters,\n },\n }\n const { data: queryData, isLoading } =\n useGetFullTableQueryResults(queryRequest)\n\n if (isLoading) {\n return <Skeleton width={'100%'} height={'200px'} />\n }\n if (!queryData) {\n return <></>\n }\n\n const {\n title,\n xtitle,\n ytitle,\n type,\n xaxistype,\n showlegend,\n barmode,\n horizontal,\n displayModeBar,\n hoverinfo,\n hideYAxisTickLabels,\n hideXAxisTickLabels,\n footnote,\n } = props.synapsePlotWidgetParams\n\n const config: Partial<Plotly.Config> = {\n displayModeBar,\n }\n const layout: Partial<Plotly.Layout> = {\n showlegend: showlegend,\n title,\n barmode: barmode,\n ...(footnote && { margin: { b: 40 } }),\n }\n if (xtitle || hideXAxisTickLabels) {\n layout.xaxis = {\n ...(xtitle && { title: xtitle }),\n ...(hideXAxisTickLabels && { showticklabels: false }),\n }\n }\n if (xaxistype) {\n layout.xaxis = {\n ...layout.xaxis,\n type: xaxistype,\n }\n }\n if (ytitle || hideYAxisTickLabels) {\n layout.yaxis = {\n ...(ytitle && { title: ytitle }),\n ...(hideYAxisTickLabels && { showticklabels: false }),\n }\n }\n // init plot_data\n const plotData: Partial<Plotly.PlotData>[] = []\n const orientation = horizontal ? 'h' : 'v'\n const headers = queryData.queryResult?.queryResults.headers ?? []\n const rows = queryData.queryResult?.queryResults.rows ?? []\n if (headers.length == 0 || rows.length == 0) {\n return <></>\n }\n for (let i = 0; i < headers.length - 1; i += 1) {\n // make an entry for each set of data points\n const valueColumnName = headers[i + 1].name\n const friendlyColumnName = unCamelCase(valueColumnName.replace(/_/g, ' '))\n const hovertemplate =\n type === 'bar'\n ? horizontal\n ? `<b>%{y}</b><br>${friendlyColumnName}: %{x:,}<extra></extra>`\n : `<b>%{x}</b><br>${friendlyColumnName}: %{y:,}<extra></extra>`\n : undefined\n plotData[i] = {\n orientation,\n name: valueColumnName,\n type: type,\n x: [],\n y: [],\n customdata: [],\n ...(hovertemplate !== undefined && { hovertemplate }),\n ...(hoverinfo !== undefined && { hoverinfo }),\n }\n }\n // grab all the data\n for (const row of rows) {\n for (let j = 1; j < row.values.length; j += 1) {\n // create pairs of data\n const rowValues = row.values\n const xArray = plotData[j - 1].x as Plotly.Datum[]\n const yArray = plotData[j - 1].y as Plotly.Datum[]\n const customdata = plotData[j - 1].customdata as Plotly.Datum[]\n\n xArray.push(horizontal ? rowValues[j] : rowValues[0])\n yArray.push(horizontal ? rowValues[0] : rowValues[j])\n customdata.push(JSON.stringify(row))\n }\n }\n let onPlotClick:\n | ((event: Readonly<Plotly.PlotMouseEvent>) => void)\n | undefined\n if (onCustomPlotClick) {\n onPlotClick = eventData => {\n const selectedRow = JSON.parse(\n eventData.points[0].customdata as string,\n ) as Row\n onCustomPlotClick({\n row: selectedRow,\n queryContext,\n })\n }\n }\n\n return (\n <Plot\n style={{ width: '100%', height: '450px' }}\n className={onPlotClick ? 'SynapsePlot--clickable' : undefined}\n layout={layout}\n data={plotData}\n config={config}\n onClick={onPlotClick}\n useResizeHandler={true}\n />\n )\n}\n\nexport default SynapsePlot\n"],"mappings":";;;;;;;;;;;;AAgDA,IAAa,KAAe,MAA6C;CACvE,IAAM,EAAE,aAAU,EAAM,yBAClB,EAAE,mBAAgB,sBAAmB,sBAAmB,oBAC5D,EAAM,oBAAoB,CAAC,GAWvB,EAAE,MAAM,GAAW,iBACvB,EAA4B;EAV5B,cAAc;EACd,UAAU;EACV,UAAU,EAA8B,CAAK;EAC7C,OAAO;GACL,KAAK;GACL;GACA;EACF;CAG4B,CAAY;CAE1C,IAAI,GACF,OAAO,kBAAC,GAAD;EAAU,OAAO;EAAQ,QAAQ;CAAU,CAAA;CAEpD,IAAI,CAAC,GACH,OAAO,kBAAA,GAAA,CAAI,CAAA;CAGb,IAAM,EACJ,UACA,WACA,WACA,SACA,cACA,eACA,YACA,eACA,mBACA,cACA,wBACA,wBACA,gBACE,EAAM,yBAEJ,IAAiC,EACrC,kBACF,GACM,IAAiC;EACzB;EACZ;EACS;EACT,GAAI,KAAY,EAAE,QAAQ,EAAE,GAAG,GAAG,EAAE;CACtC;CAaA,CAZI,KAAU,OACZ,EAAO,QAAQ;EACb,GAAI,KAAU,EAAE,OAAO,EAAO;EAC9B,GAAI,KAAuB,EAAE,gBAAgB,GAAM;CACrD,IAEE,MACF,EAAO,QAAQ;EACb,GAAG,EAAO;EACV,MAAM;CACR,KAEE,KAAU,OACZ,EAAO,QAAQ;EACb,GAAI,KAAU,EAAE,OAAO,EAAO;EAC9B,GAAI,KAAuB,EAAE,gBAAgB,GAAM;CACrD;CAGF,IAAM,IAAuC,CAAC,GACxC,IAAc,IAAa,MAAM,KACjC,IAAU,EAAU,aAAa,aAAa,WAAW,CAAC,GAC1D,IAAO,EAAU,aAAa,aAAa,QAAQ,CAAC;CAC1D,IAAI,EAAQ,UAAU,KAAK,EAAK,UAAU,GACxC,OAAO,kBAAA,GAAA,CAAI,CAAA;CAEb,KAAK,IAAI,IAAI,GAAG,IAAI,EAAQ,SAAS,GAAG,KAAK,GAAG;EAE9C,IAAM,IAAkB,EAAQ,IAAI,GAAG,MACjC,IAAqB,EAAY,EAAgB,QAAQ,MAAM,GAAG,CAAC,GACnE,IACJ,MAAS,QACL,IACE,kBAAkB,EAAmB,2BACrC,kBAAkB,EAAmB,2BACvC,KAAA;EACN,EAAS,KAAK;GACZ;GACA,MAAM;GACA;GACN,GAAG,CAAC;GACJ,GAAG,CAAC;GACJ,YAAY,CAAC;GACb,GAAI,MAAkB,KAAA,KAAa,EAAE,iBAAc;GACnD,GAAI,MAAc,KAAA,KAAa,EAAE,aAAU;EAC7C;CACF;CAEA,KAAK,IAAM,KAAO,GAChB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAI,OAAO,QAAQ,KAAK,GAAG;EAE7C,IAAM,IAAY,EAAI,QAChB,IAAS,EAAS,IAAI,GAAG,GACzB,IAAS,EAAS,IAAI,GAAG,GACzB,IAAa,EAAS,IAAI,GAAG;EAInC,AAFA,EAAO,KAAK,IAAa,EAAU,KAAK,EAAU,EAAE,GACpD,EAAO,KAAK,IAAa,EAAU,KAAK,EAAU,EAAE,GACpD,EAAW,KAAK,KAAK,UAAU,CAAG,CAAC;CACrC;CAEF,IAAI;CAeJ,OAZI,MACF,KAAc,MAAa;EAIzB,EAAkB;GAChB,KAJkB,KAAK,MACvB,EAAU,OAAO,GAAG,UAGf;GACL;EACF,CAAC;CACH,IAIA,kBAAC,GAAD;EACE,OAAO;GAAE,OAAO;GAAQ,QAAQ;EAAQ;EACxC,WAAW,IAAc,2BAA2B,KAAA;EAC5C;EACR,MAAM;EACE;EACR,SAAS;EACT,kBAAkB;CACnB,CAAA;AAEL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpsetPlot.js","names":[],"sources":["../../../src/components/Plot/UpsetPlot.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport LargeButton from '@/components/styled/LargeButton'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { parseEntityIdFromSqlStatement } from '@/utils/functions/SqlFunctions'\nimport { QueryBundleRequest } from '@sage-bionetworks/synapse-types'\nimport { ReactComponent as DiagonalLinePattern } from '@/assets/illustrations/diagonalLinePattern.svg'\n\nimport UpSetJS, {\n extractSets,\n generateCombinations,\n ISet,\n ISetCombinations,\n ISetLike,\n ISets,\n UpSetFontSizes,\n UpSetSelectionProps,\n} from '@upsetjs/react'\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\nimport { useEffect, useState } from 'react'\nimport { getColorPalette } from '../ColorGradient/ColorGradient'\nimport { ErrorBanner } from '../error/ErrorBanner'\nimport loadingScreen from '../LoadingScreen/LoadingScreen'\nimport { Box, SxProps, useTheme } from '@mui/material'\nimport { useMeasure } from '@react-hookz/web'\n\nexport type UpsetPlotProps = {\n sql: string // first column should contain values, second column should contain a single set value. ie. SELECT distinct individualID, assay FROM syn20821313\n rgbIndex?: number // color plot based on portal\n customPlotColor?: string\n selectionOpacity?: number\n variant?: 'ampals'\n maxBarCount?: number // will show all if not set\n setName?: string // instead of \"Set Size\"\n combinationName?: string // instead of \"Intersection Size\"\n height?: number\n summaryLinkText?: string // text for home page link below chart\n summaryLink?: string // url for home page link below chart\n} & Pick<UpSetSelectionProps, 'onClick'>\n\nexport type UpsetPlotData = {\n sets: ISets<any>\n combinations: ISetCombinations<any>\n}\n/**\n * Upset plot. See https://medium.com/@sgratzl/upset-js-javascript-tutorial-1b84bfd6896d\n * Currently this is driven by a Synapse Table/View, but we may want to alter it to read in the data from a flat file instead.\n */\nexport function UpsetPlot({\n sql,\n variant,\n rgbIndex,\n maxBarCount,\n setName,\n combinationName,\n height = 700,\n summaryLinkText,\n summaryLink,\n onClick,\n customPlotColor,\n selectionOpacity,\n}: UpsetPlotProps) {\n const { accessToken } = useSynapseContext()\n const [isLoading, setIsLoading] = useState<boolean>()\n const [data, setData] = useState<UpsetPlotData>()\n const [error, setError] = useState<string>()\n const [selection, setSelection] = useState(null as ISetLike<any> | null)\n const theme = useTheme()\n\n const [plotContainerMeasurements, plotContainerRef] = useMeasure()\n\n const variantStyles: Record<string, SxProps> = {\n default: {\n 'text[class^=\"sChartTextStyle-upset-\"], text[class^= \"cChartTextStyle-upset-\"]':\n {\n textTransform: 'uppercase',\n },\n },\n ampals: {\n width: '100%',\n '& .selectionHint-upset-': {\n stroke: 'transparent !important',\n },\n '& [class^=\"selectionHint-upset-\"]': {\n stroke: 'transparent !important',\n },\n '& g[class^=\"interactive-upset-\"]:hover line[class^=\"upsetLine-\"]': {\n stroke: theme.palette.primary.main,\n strokeWidth: '4px',\n strokeOpacity: 1,\n },\n '& g[class^=\"interactive-upset-\"]:hover circle[class^=\"fillNotMember-upset-\"]':\n {\n opacity: 0.2,\n },\n '& line[class^=\"upsetLine-\"]': {\n strokeOpacity: 0,\n },\n '& rect[class^=\"fillPrimary-upset-\"]': {\n fill: 'url(#diagonalLinePattern)',\n stroke: theme.palette.grey[700],\n strokeWidth: 2,\n },\n '& circle[class^=\"fillNotMember-upset-\"]': {\n stroke: theme.palette.grey[600],\n strokeWidth: 2,\n },\n '& text[class^=\"sChartTextStyle-upset-\"], & text[class^=\"cChartTextStyle-upset-\"]':\n {\n fontWeight: 700,\n fill: theme.palette.grey[800],\n lineHeight: '150%',\n },\n '& text[class^=\"cChartTextStyle-upset-\"]': {\n transform: 'translate(-60px, 190px) rotate(-90deg)',\n },\n '& text[class^=\"sChartTextStyle-upset-\"]': {\n transform: 'translate(-10px, 100px) rotate(-90deg)',\n },\n },\n }\n\n let plotColor: string, selectionColor: string\n if (rgbIndex != null) {\n const { colorPalette } = getColorPalette(rgbIndex, 2)\n plotColor = colorPalette[0]\n selectionColor = colorPalette[0]\n } else {\n plotColor = theme.palette.grey[700]\n selectionColor = customPlotColor || theme.palette.primary.main\n }\n\n const updateFontSizes: UpSetFontSizes = {\n setLabel: '14px',\n }\n\n useEffect(() => {\n let isCancelled: boolean = false\n const getPlotData = async () => {\n setIsLoading(true)\n try {\n const entityId = parseEntityIdFromSqlStatement(sql)\n const partMask = SynapseConstants.BUNDLE_MASK_QUERY_RESULTS\n const queryRequest: QueryBundleRequest = {\n partMask,\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId,\n query: {\n sql,\n },\n }\n const { queryResult } = await SynapseClient.getFullQueryTableResults(\n queryRequest,\n accessToken,\n )\n // transform query data into plot data, and store.\n // collect all values for each key\n const keyValuesMap: Record<string, { name: string; sets: string[] }> =\n {}\n // keyValuesMap looks like { 'A': {name: 'A', sets: ['S1', 'S2'] }, 'B': { name: 'B', sets: ['S1'] }, ... }\n // It's a little redundant, but makes the next step much easier.\n\n const caseInsensitiveSetNames: Record<string, string> = {}\n // caseInsensitiveSetNames looks like { 'RNASEQ': 'rnaSeq', 'NOMe-SEQ': 'NOMe-seq'}.\n\n for (const row of queryResult?.queryResults.rows ?? []) {\n for (let j = 1; j < row.values.length; j += 1) {\n const rowValues: string[] = row.values as string[]\n if (rowValues.some(value => value === null)) {\n console.warn('Row has null value(s) when no nulls expected')\n }\n\n const key = rowValues[0]\n let newValue = rowValues[j]\n keyValuesMap[key] = keyValuesMap[key] || {}\n keyValuesMap[key].name = key\n\n keyValuesMap[key].sets = keyValuesMap[key].sets || []\n const found = keyValuesMap[key].sets.find((v: any) => v == newValue)\n if (!found && newValue) {\n if (caseInsensitiveSetNames[newValue.toUpperCase()]) {\n newValue = caseInsensitiveSetNames[newValue.toUpperCase()]\n } else {\n caseInsensitiveSetNames[newValue.toUpperCase()] = newValue\n }\n keyValuesMap[key].sets.push(newValue)\n }\n }\n }\n // now create the expected elems set\n const elems = Object.values(keyValuesMap)\n // elems looks like [{ name: 'A', sets: ['S1', 'S2'] }, { name: 'B', sets: ['S1'] }, ...]\n const sets = extractSets(elems)\n // PORTALS-1673: sort by assay name\n const sortedSets = (sets as ISet[]).sort((a, b) => {\n return ('' + b.name).localeCompare(a.name)\n })\n\n const combinations = generateCombinations(sortedSets, {\n type: 'intersection',\n min: 1,\n limit: maxBarCount,\n order: 'cardinality:desc',\n })\n if (!isCancelled) {\n setData({ sets, combinations })\n setIsLoading(false)\n }\n } catch (err) {\n if (!isCancelled) {\n setIsLoading(false)\n let errorMessage = err['reason']\n if (!errorMessage) {\n console.error(err)\n errorMessage = 'Error retrieving data'\n }\n setError(errorMessage)\n }\n }\n }\n getPlotData()\n return () => {\n isCancelled = true\n }\n }, [sql, accessToken])\n\n return (\n <>\n {isLoading && loadingScreen}\n {!isLoading && data && (\n <>\n {variantStyles['ampals'] && (\n // Render DiagonalLinePattern so UpsetPlot can use `fill: url(#diagonalLinePattern)`.\n // The SVG element must be added to the DOM so we can reference its ID (which is set in the SVG file) in the `fill` style\n // Ideally, we would reference the asset URL imported by Vite, but this isn't working.\n <Box\n sx={{\n '& path': {\n stroke: '#D9D9D9 !important',\n },\n width: 0,\n height: 0,\n position: 'absolute',\n }}\n >\n <DiagonalLinePattern />\n </Box>\n )}\n <Box\n className=\"UpsetPlot\"\n sx={variant ? variantStyles[variant] : variantStyles['default']}\n ref={plotContainerRef}\n >\n <UpSetJS\n sets={data.sets}\n combinations={data.combinations}\n width={plotContainerMeasurements?.width || 800}\n height={height}\n onHover={setSelection}\n onClick={onClick}\n selection={selection}\n color={plotColor}\n selectionColor={selectionColor}\n hoverHintColor={'orange'}\n hasSelectionOpacity={selectionOpacity ? selectionOpacity : 0.3}\n // alternatingBackgroundColor={false}\n setName={setName}\n combinationName={combinationName}\n fontFamily=\"'DM Sans', sans-serif\"\n fontSizes={updateFontSizes}\n exportButtons={false}\n notMemberColor=\"transparent\"\n />\n {summaryLink && summaryLinkText && (\n <div className=\"UpsetPlot__summary\">\n <LargeButton\n color=\"secondary\"\n variant=\"contained\"\n href={summaryLink}\n >\n {summaryLinkText}\n </LargeButton>\n </div>\n )}\n </Box>\n </>\n )}\n <ErrorBanner error={error} />\n </>\n )\n}\n\nexport default UpsetPlot\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UpsetPlot.js","names":[],"sources":["../../../src/components/Plot/UpsetPlot.tsx"],"sourcesContent":["/* oxlint-disable @typescript-eslint/no-explicit-any */\nimport LargeButton from '@/components/styled/LargeButton'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { parseEntityIdFromSqlStatement } from '@/utils/functions/SqlFunctions'\nimport { QueryBundleRequest } from '@sage-bionetworks/synapse-types'\nimport { ReactComponent as DiagonalLinePattern } from '@/assets/illustrations/diagonalLinePattern.svg'\n\nimport UpSetJS, {\n extractSets,\n generateCombinations,\n ISet,\n ISetCombinations,\n ISetLike,\n ISets,\n UpSetFontSizes,\n UpSetSelectionProps,\n} from '@upsetjs/react'\n/* oxlint-disable @typescript-eslint/no-unsafe-member-access */\n/* oxlint-disable @typescript-eslint/no-unsafe-assignment */\nimport { useEffect, useState } from 'react'\nimport { getColorPalette } from '../ColorGradient/ColorGradient'\nimport { ErrorBanner } from '../error/ErrorBanner'\nimport loadingScreen from '../LoadingScreen/LoadingScreen'\nimport { Box, SxProps, useTheme } from '@mui/material'\nimport { useMeasure } from '@react-hookz/web'\n\nexport type UpsetPlotProps = {\n sql: string // first column should contain values, second column should contain a single set value. ie. SELECT distinct individualID, assay FROM syn20821313\n rgbIndex?: number // color plot based on portal\n customPlotColor?: string\n selectionOpacity?: number\n variant?: 'ampals'\n maxBarCount?: number // will show all if not set\n setName?: string // instead of \"Set Size\"\n combinationName?: string // instead of \"Intersection Size\"\n height?: number\n summaryLinkText?: string // text for home page link below chart\n summaryLink?: string // url for home page link below chart\n} & Pick<UpSetSelectionProps, 'onClick'>\n\nexport type UpsetPlotData = {\n sets: ISets<any>\n combinations: ISetCombinations<any>\n}\n/**\n * Upset plot. See https://medium.com/@sgratzl/upset-js-javascript-tutorial-1b84bfd6896d\n * Currently this is driven by a Synapse Table/View, but we may want to alter it to read in the data from a flat file instead.\n */\nexport function UpsetPlot({\n sql,\n variant,\n rgbIndex,\n maxBarCount,\n setName,\n combinationName,\n height = 700,\n summaryLinkText,\n summaryLink,\n onClick,\n customPlotColor,\n selectionOpacity,\n}: UpsetPlotProps) {\n const { accessToken } = useSynapseContext()\n const [isLoading, setIsLoading] = useState<boolean>()\n const [data, setData] = useState<UpsetPlotData>()\n const [error, setError] = useState<string>()\n const [selection, setSelection] = useState(null as ISetLike<any> | null)\n const theme = useTheme()\n\n const [plotContainerMeasurements, plotContainerRef] = useMeasure()\n\n const variantStyles: Record<string, SxProps> = {\n default: {\n 'text[class^=\"sChartTextStyle-upset-\"], text[class^= \"cChartTextStyle-upset-\"]':\n {\n textTransform: 'uppercase',\n },\n },\n ampals: {\n width: '100%',\n '& .selectionHint-upset-': {\n stroke: 'transparent !important',\n },\n '& [class^=\"selectionHint-upset-\"]': {\n stroke: 'transparent !important',\n },\n '& g[class^=\"interactive-upset-\"]:hover line[class^=\"upsetLine-\"]': {\n stroke: theme.palette.primary.main,\n strokeWidth: '4px',\n strokeOpacity: 1,\n },\n '& g[class^=\"interactive-upset-\"]:hover circle[class^=\"fillNotMember-upset-\"]':\n {\n opacity: 0.2,\n },\n '& line[class^=\"upsetLine-\"]': {\n strokeOpacity: 0,\n },\n '& rect[class^=\"fillPrimary-upset-\"]': {\n fill: 'url(#diagonalLinePattern)',\n stroke: theme.palette.grey[700],\n strokeWidth: 2,\n },\n '& circle[class^=\"fillNotMember-upset-\"]': {\n stroke: theme.palette.grey[600],\n strokeWidth: 2,\n },\n '& text[class^=\"sChartTextStyle-upset-\"], & text[class^=\"cChartTextStyle-upset-\"]':\n {\n fontWeight: 700,\n fill: theme.palette.grey[800],\n lineHeight: '150%',\n },\n '& text[class^=\"cChartTextStyle-upset-\"]': {\n transform: 'translate(-60px, 190px) rotate(-90deg)',\n },\n '& text[class^=\"sChartTextStyle-upset-\"]': {\n transform: 'translate(-10px, 100px) rotate(-90deg)',\n },\n },\n }\n\n let plotColor: string, selectionColor: string\n if (rgbIndex != null) {\n const { colorPalette } = getColorPalette(rgbIndex, 2)\n plotColor = colorPalette[0]\n selectionColor = colorPalette[0]\n } else {\n plotColor = theme.palette.grey[700]\n selectionColor = customPlotColor || theme.palette.primary.main\n }\n\n const updateFontSizes: UpSetFontSizes = {\n setLabel: '14px',\n }\n\n useEffect(() => {\n let isCancelled: boolean = false\n const getPlotData = async () => {\n setIsLoading(true)\n try {\n const entityId = parseEntityIdFromSqlStatement(sql)\n const partMask = SynapseConstants.BUNDLE_MASK_QUERY_RESULTS\n const queryRequest: QueryBundleRequest = {\n partMask,\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId,\n query: {\n sql,\n },\n }\n const { queryResult } = await SynapseClient.getFullQueryTableResults(\n queryRequest,\n accessToken,\n )\n // transform query data into plot data, and store.\n // collect all values for each key\n const keyValuesMap: Record<string, { name: string; sets: string[] }> =\n {}\n // keyValuesMap looks like { 'A': {name: 'A', sets: ['S1', 'S2'] }, 'B': { name: 'B', sets: ['S1'] }, ... }\n // It's a little redundant, but makes the next step much easier.\n\n const caseInsensitiveSetNames: Record<string, string> = {}\n // caseInsensitiveSetNames looks like { 'RNASEQ': 'rnaSeq', 'NOMe-SEQ': 'NOMe-seq'}.\n\n for (const row of queryResult?.queryResults.rows ?? []) {\n for (let j = 1; j < row.values.length; j += 1) {\n const rowValues: string[] = row.values as string[]\n if (rowValues.some(value => value === null)) {\n console.warn('Row has null value(s) when no nulls expected')\n }\n\n const key = rowValues[0]\n let newValue = rowValues[j]\n keyValuesMap[key] = keyValuesMap[key] || {}\n keyValuesMap[key].name = key\n\n keyValuesMap[key].sets = keyValuesMap[key].sets || []\n const found = keyValuesMap[key].sets.find((v: any) => v == newValue)\n if (!found && newValue) {\n if (caseInsensitiveSetNames[newValue.toUpperCase()]) {\n newValue = caseInsensitiveSetNames[newValue.toUpperCase()]\n } else {\n caseInsensitiveSetNames[newValue.toUpperCase()] = newValue\n }\n keyValuesMap[key].sets.push(newValue)\n }\n }\n }\n // now create the expected elems set\n const elems = Object.values(keyValuesMap)\n // elems looks like [{ name: 'A', sets: ['S1', 'S2'] }, { name: 'B', sets: ['S1'] }, ...]\n const sets = extractSets(elems)\n // PORTALS-1673: sort by assay name\n const sortedSets = (sets as ISet[]).sort((a, b) => {\n return ('' + b.name).localeCompare(a.name)\n })\n\n const combinations = generateCombinations(sortedSets, {\n type: 'intersection',\n min: 1,\n limit: maxBarCount,\n order: 'cardinality:desc',\n })\n if (!isCancelled) {\n setData({ sets, combinations })\n setIsLoading(false)\n }\n } catch (err) {\n if (!isCancelled) {\n setIsLoading(false)\n let errorMessage = err['reason']\n if (!errorMessage) {\n console.error(err)\n errorMessage = 'Error retrieving data'\n }\n setError(errorMessage)\n }\n }\n }\n getPlotData()\n return () => {\n isCancelled = true\n }\n }, [sql, accessToken])\n\n return (\n <>\n {isLoading && loadingScreen}\n {!isLoading && data && (\n <>\n {variantStyles['ampals'] && (\n // Render DiagonalLinePattern so UpsetPlot can use `fill: url(#diagonalLinePattern)`.\n // The SVG element must be added to the DOM so we can reference its ID (which is set in the SVG file) in the `fill` style\n // Ideally, we would reference the asset URL imported by Vite, but this isn't working.\n <Box\n sx={{\n '& path': {\n stroke: '#D9D9D9 !important',\n },\n width: 0,\n height: 0,\n position: 'absolute',\n }}\n >\n <DiagonalLinePattern />\n </Box>\n )}\n <Box\n className=\"UpsetPlot\"\n sx={variant ? variantStyles[variant] : variantStyles['default']}\n ref={plotContainerRef}\n >\n <UpSetJS\n sets={data.sets}\n combinations={data.combinations}\n width={plotContainerMeasurements?.width || 800}\n height={height}\n onHover={setSelection}\n onClick={onClick}\n selection={selection}\n color={plotColor}\n selectionColor={selectionColor}\n hoverHintColor={'orange'}\n hasSelectionOpacity={selectionOpacity ? selectionOpacity : 0.3}\n // alternatingBackgroundColor={false}\n setName={setName}\n combinationName={combinationName}\n fontFamily=\"'DM Sans', sans-serif\"\n fontSizes={updateFontSizes}\n exportButtons={false}\n notMemberColor=\"transparent\"\n />\n {summaryLink && summaryLinkText && (\n <div className=\"UpsetPlot__summary\">\n <LargeButton\n color=\"secondary\"\n variant=\"contained\"\n href={summaryLink}\n >\n {summaryLinkText}\n </LargeButton>\n </div>\n )}\n </Box>\n </>\n )}\n <ErrorBanner error={error} />\n </>\n )\n}\n\nexport default UpsetPlot\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryWrapperSynapsePlot.d.ts","sourceRoot":"","sources":["../../../src/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"QueryWrapperSynapsePlot.d.ts","sourceRoot":"","sources":["../../../src/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAmB,MAAM,iBAAiB,CAAA;AACnE,OAAO,EACL,mCAAmC,EACnC,uBAAuB,EACxB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAGL,GAAG,EACJ,MAAM,iCAAiC,CAAA;AAKxC,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAC7C,mCAAmC,EACnC,mBAAmB,CACpB,GACC,IAAI,CAAC,uBAAuB,EAAE,gBAAgB,GAAG,mBAAmB,CAAC,CAAA;AACvE,KAAK,oCAAoC,GAAG;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,CAAA;AACD,MAAM,MAAM,oCAAoC,GAAG;IACjD,GAAG,EAAE,GAAG,CAAA;IACR,YAAY,CAAC,EAAE,gBAAgB,CAAA;CAChC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAC7C,KAAK,EAAE,4BAA4B,GAAG,oCAAoC,2CAoG3E"}
|
|
@@ -1,45 +1,88 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConfirmationDialog as e } from "../ConfirmationDialog/ConfirmationDialog.js";
|
|
2
|
+
import { StyledFormControl as t } from "../styled/StyledFormControl.js";
|
|
3
|
+
import { useQueryContext as n } from "../QueryContext/QueryContext.js";
|
|
2
4
|
import "../QueryContext/index.js";
|
|
3
|
-
import { useQueryVisualizationContext as
|
|
5
|
+
import { useQueryVisualizationContext as r } from "../QueryVisualizationWrapper/QueryVisualizationContext.js";
|
|
4
6
|
import "../QueryVisualizationWrapper/index.js";
|
|
5
|
-
import
|
|
6
|
-
import { SynapsePlot as
|
|
7
|
+
import i from "../Plot/PlotPanelHeader.js";
|
|
8
|
+
import { SynapsePlot as a } from "../Plot/SynapsePlot.js";
|
|
7
9
|
import "../Plot/index.js";
|
|
8
|
-
import { useMemo as
|
|
9
|
-
import {
|
|
10
|
+
import { useId as o, useMemo as s, useState as c } from "react";
|
|
11
|
+
import { InputLabel as l, MenuItem as u, Select as d, Stack as f, Typography as p } from "@mui/material";
|
|
12
|
+
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
10
13
|
//#region src/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.tsx
|
|
11
|
-
function
|
|
12
|
-
let
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
additionalFilters: t
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
function _(_) {
|
|
15
|
+
let v = n(), { currentQueryRequest: y } = v, { title: b, footnote: x, onCustomPlotClick: S, onHide: C } = _, { showPlots: w } = r(), [T, E] = c(!1), [D, O] = c(_.xaxistype), k = _.xaxistype === "log", A = o(), j = /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(a, {
|
|
16
|
+
synapsePlotWidgetParams: s(() => ({
|
|
17
|
+
..._,
|
|
18
|
+
xaxistype: D,
|
|
19
|
+
title: void 0,
|
|
20
|
+
showlegend: !1,
|
|
21
|
+
displayModebar: !1
|
|
22
|
+
}), [_, D]),
|
|
23
|
+
customPlotParams: s(() => {
|
|
24
|
+
let { selectedFacets: e, additionalFilters: t } = y.query;
|
|
25
|
+
return {
|
|
26
|
+
selectedFacets: e,
|
|
27
|
+
additionalFilters: t,
|
|
28
|
+
onCustomPlotClick: S,
|
|
29
|
+
queryContext: v
|
|
30
|
+
};
|
|
31
|
+
}, [
|
|
32
|
+
y.query,
|
|
33
|
+
S,
|
|
34
|
+
v
|
|
35
|
+
])
|
|
36
|
+
}), x && /* @__PURE__ */ h(p, {
|
|
37
|
+
variant: "body1Italic",
|
|
38
|
+
sx: {
|
|
39
|
+
display: "block",
|
|
40
|
+
mt: 1,
|
|
41
|
+
px: 1
|
|
42
|
+
},
|
|
43
|
+
children: x
|
|
44
|
+
})] });
|
|
45
|
+
return /* @__PURE__ */ g("div", {
|
|
31
46
|
role: "figure",
|
|
32
47
|
className: "SynapsePlot",
|
|
33
|
-
children:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
children: [/* @__PURE__ */ h(e, {
|
|
49
|
+
open: T,
|
|
50
|
+
onCancel: () => E(!1),
|
|
51
|
+
title: b ?? "",
|
|
52
|
+
content: /* @__PURE__ */ g(f, {
|
|
53
|
+
sx: { gap: 2 },
|
|
54
|
+
children: [k && /* @__PURE__ */ g(t, {
|
|
55
|
+
fullWidth: !0,
|
|
56
|
+
children: [/* @__PURE__ */ h(l, {
|
|
57
|
+
id: A,
|
|
58
|
+
children: "X Axis Scale"
|
|
59
|
+
}), /* @__PURE__ */ g(d, {
|
|
60
|
+
labelId: A,
|
|
61
|
+
label: "X Axis Scale",
|
|
62
|
+
value: D ?? "log",
|
|
63
|
+
onChange: (e) => O(e.target.value),
|
|
64
|
+
children: [/* @__PURE__ */ h(u, {
|
|
65
|
+
value: "log",
|
|
66
|
+
children: "Log"
|
|
67
|
+
}), /* @__PURE__ */ h(u, {
|
|
68
|
+
value: "linear",
|
|
69
|
+
children: "Linear"
|
|
70
|
+
})]
|
|
71
|
+
})]
|
|
72
|
+
}), j]
|
|
73
|
+
}),
|
|
74
|
+
hasCancelButton: !1,
|
|
75
|
+
confirmButtonProps: { children: "Close" },
|
|
76
|
+
onConfirm: () => E(!1),
|
|
77
|
+
maxWidth: "md"
|
|
78
|
+
}), w && /* @__PURE__ */ g(m, { children: [b && /* @__PURE__ */ h(i, {
|
|
79
|
+
title: b,
|
|
80
|
+
onHide: C,
|
|
81
|
+
setShowModal: E
|
|
82
|
+
}), j] })]
|
|
40
83
|
});
|
|
41
84
|
}
|
|
42
85
|
//#endregion
|
|
43
|
-
export {
|
|
86
|
+
export { _ as default };
|
|
44
87
|
|
|
45
88
|
//# sourceMappingURL=QueryWrapperSynapsePlot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryWrapperSynapsePlot.js","names":[],"sources":["../../../src/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.tsx"],"sourcesContent":["import { useMemo } from 'react'\nimport { SynapsePlot } from '../Plot'\nimport { QueryContextType, useQueryContext } from '../QueryContext'\nimport {\n QueryWrapperPlotNavCustomPlotParams,\n SynapsePlotWidgetParams,\n} from '../Plot/SynapsePlot'\nimport {\n FacetColumnRequest,\n QueryFilter,\n Row,\n} from '@sage-bionetworks/synapse-types'\nimport { useQueryVisualizationContext } from '../QueryVisualizationWrapper'\nimport PlotPanelHeader from '../Plot/PlotPanelHeader'\nexport type QueryWrapperSynapsePlotProps = Pick<\n QueryWrapperPlotNavCustomPlotParams,\n 'onCustomPlotClick'\n> &\n Omit<SynapsePlotWidgetParams, 'selectedFacets' | 'additionalFilters'>\ntype QueryWrapperSynapsePlotInternalProps = {\n onHide: () => void\n}\nexport type QueryWrapperSynapsePlotRowClickEvent = {\n row: Row\n queryContext?: QueryContextType\n}\n\n/**\n * This component wraps a SynapsePlot. It passes in the current query context, and has a custom\n * \"title\" style so that it looks like it belongs in the QueryWrapperPlotNav plots.\n * @param props\n * @returns\n */\nexport default function QueryWrapperSynapsePlot(\n props: QueryWrapperSynapsePlotProps & QueryWrapperSynapsePlotInternalProps,\n) {\n const queryContext = useQueryContext()\n const { currentQueryRequest } = queryContext\n const { title, onCustomPlotClick, onHide } = props\n const { showPlots } = useQueryVisualizationContext()\n\n const widgetParamsMapped: SynapsePlotWidgetParams = useMemo(() => {\n return {\n ...props,\n title: undefined, // we are handling the plot title here in this component\n showlegend: false,\n displayModebar: false,\n }\n }, [props])\n\n const customPlotParams: QueryWrapperPlotNavCustomPlotParams = useMemo(() => {\n const { selectedFacets, additionalFilters } = currentQueryRequest.query\n return {\n selectedFacets: selectedFacets as FacetColumnRequest[],\n additionalFilters: additionalFilters as QueryFilter[],\n onCustomPlotClick,\n queryContext,\n }\n }, [currentQueryRequest.query, onCustomPlotClick, queryContext])\n return (\n <div role=\"figure\" className=\"SynapsePlot\">\n {showPlots && (\n <>\n {title && <PlotPanelHeader
|
|
1
|
+
{"version":3,"file":"QueryWrapperSynapsePlot.js","names":[],"sources":["../../../src/components/QueryWrapperPlotNav/QueryWrapperSynapsePlot.tsx"],"sourcesContent":["import { useId, useMemo, useState } from 'react'\nimport { InputLabel, MenuItem, Select, Stack, Typography } from '@mui/material'\nimport { AxisType } from 'plotly.js-basic-dist'\nimport { SynapsePlot } from '../Plot'\nimport { QueryContextType, useQueryContext } from '../QueryContext'\nimport {\n QueryWrapperPlotNavCustomPlotParams,\n SynapsePlotWidgetParams,\n} from '../Plot/SynapsePlot'\nimport {\n FacetColumnRequest,\n QueryFilter,\n Row,\n} from '@sage-bionetworks/synapse-types'\nimport { useQueryVisualizationContext } from '../QueryVisualizationWrapper'\nimport PlotPanelHeader from '../Plot/PlotPanelHeader'\nimport { ConfirmationDialog } from '../ConfirmationDialog/ConfirmationDialog'\nimport StyledFormControl from '../styled/StyledFormControl'\nexport type QueryWrapperSynapsePlotProps = Pick<\n QueryWrapperPlotNavCustomPlotParams,\n 'onCustomPlotClick'\n> &\n Omit<SynapsePlotWidgetParams, 'selectedFacets' | 'additionalFilters'>\ntype QueryWrapperSynapsePlotInternalProps = {\n onHide: () => void\n}\nexport type QueryWrapperSynapsePlotRowClickEvent = {\n row: Row\n queryContext?: QueryContextType\n}\n\n/**\n * This component wraps a SynapsePlot. It passes in the current query context, and has a custom\n * \"title\" style so that it looks like it belongs in the QueryWrapperPlotNav plots.\n * @param props\n * @returns\n */\nexport default function QueryWrapperSynapsePlot(\n props: QueryWrapperSynapsePlotProps & QueryWrapperSynapsePlotInternalProps,\n) {\n const queryContext = useQueryContext()\n const { currentQueryRequest } = queryContext\n const { title, footnote, onCustomPlotClick, onHide } = props\n const { showPlots } = useQueryVisualizationContext()\n\n const [showModal, setShowModal] = useState(false)\n const [currentXAxisType, setCurrentXAxisType] = useState<\n AxisType | undefined\n >(props.xaxistype)\n\n // Only offer log/linear toggle when the default axis type was explicitly set to 'log'\n const defaultXAxisTypeIsLog = props.xaxistype === 'log'\n\n const xAxisTypeInputLabelId = useId()\n\n const widgetParamsMapped: SynapsePlotWidgetParams = useMemo(() => {\n return {\n ...props,\n xaxistype: currentXAxisType,\n title: undefined, // we are handling the plot title here in this component\n showlegend: false,\n displayModebar: false,\n }\n }, [props, currentXAxisType])\n\n const customPlotParams: QueryWrapperPlotNavCustomPlotParams = useMemo(() => {\n const { selectedFacets, additionalFilters } = currentQueryRequest.query\n return {\n selectedFacets: selectedFacets as FacetColumnRequest[],\n additionalFilters: additionalFilters as QueryFilter[],\n onCustomPlotClick,\n queryContext,\n }\n }, [currentQueryRequest.query, onCustomPlotClick, queryContext])\n\n const plotContent = (\n <>\n <SynapsePlot\n synapsePlotWidgetParams={widgetParamsMapped}\n customPlotParams={customPlotParams}\n />\n {footnote && (\n <Typography\n variant=\"body1Italic\"\n sx={{ display: 'block', mt: 1, px: 1 }}\n >\n {footnote}\n </Typography>\n )}\n </>\n )\n\n const modalContent = (\n <Stack sx={{ gap: 2 }}>\n {defaultXAxisTypeIsLog && (\n <StyledFormControl fullWidth>\n <InputLabel id={xAxisTypeInputLabelId}>X Axis Scale</InputLabel>\n <Select\n labelId={xAxisTypeInputLabelId}\n label=\"X Axis Scale\"\n value={currentXAxisType ?? 'log'}\n onChange={e => setCurrentXAxisType(e.target.value as AxisType)}\n >\n <MenuItem value=\"log\">Log</MenuItem>\n <MenuItem value=\"linear\">Linear</MenuItem>\n </Select>\n </StyledFormControl>\n )}\n {plotContent}\n </Stack>\n )\n\n return (\n <div role=\"figure\" className=\"SynapsePlot\">\n <ConfirmationDialog\n open={showModal}\n onCancel={() => setShowModal(false)}\n title={title ?? ''}\n content={modalContent}\n hasCancelButton={false}\n confirmButtonProps={{ children: 'Close' }}\n onConfirm={() => setShowModal(false)}\n maxWidth=\"md\"\n />\n {showPlots && (\n <>\n {title && (\n <PlotPanelHeader\n title={title}\n onHide={onHide}\n setShowModal={setShowModal}\n />\n )}\n {plotContent}\n </>\n )}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;AAqCA,SAAwB,EACtB,GACA;CACA,IAAM,IAAe,EAAgB,GAC/B,EAAE,2BAAwB,GAC1B,EAAE,UAAO,aAAU,sBAAmB,cAAW,GACjD,EAAE,iBAAc,EAA6B,GAE7C,CAAC,GAAW,KAAgB,EAAS,EAAK,GAC1C,CAAC,GAAkB,KAAuB,EAE9C,EAAM,SAAS,GAGX,IAAwB,EAAM,cAAc,OAE5C,IAAwB,EAAM,GAsB9B,IACJ,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EACE,yBAvB8C,SAC3C;GACL,GAAG;GACH,WAAW;GACX,OAAO,KAAA;GACP,YAAY;GACZ,gBAAgB;EAClB,IACC,CAAC,GAAO,CAAgB,CAeI;EACP,kBAdsC,QAAc;GAC1E,IAAM,EAAE,mBAAgB,yBAAsB,EAAoB;GAClE,OAAO;IACW;IACG;IACnB;IACA;GACF;EACF,GAAG;GAAC,EAAoB;GAAO;GAAmB;EAAY,CAMtC;CACnB,CAAA,GACA,KACC,kBAAC,GAAD;EACE,SAAQ;EACR,IAAI;GAAE,SAAS;GAAS,IAAI;GAAG,IAAI;EAAE;YAEpC;CACS,CAAA,CAEd,EAAA,CAAA;CAuBJ,OACE,kBAAC,OAAD;EAAK,MAAK;EAAS,WAAU;YAA7B,CACE,kBAAC,GAAD;GACE,MAAM;GACN,gBAAgB,EAAa,EAAK;GAClC,OAAO,KAAS;GAChB,SAzBJ,kBAAC,GAAD;IAAO,IAAI,EAAE,KAAK,EAAE;cAApB,CACG,KACC,kBAAC,GAAD;KAAmB,WAAA;eAAnB,CACE,kBAAC,GAAD;MAAY,IAAI;gBAAuB;KAAwB,CAAA,GAC/D,kBAAC,GAAD;MACE,SAAS;MACT,OAAM;MACN,OAAO,KAAoB;MAC3B,WAAU,MAAK,EAAoB,EAAE,OAAO,KAAiB;gBAJ/D,CAME,kBAAC,GAAD;OAAU,OAAM;iBAAM;MAAa,CAAA,GACnC,kBAAC,GAAD;OAAU,OAAM;iBAAS;MAAgB,CAAA,CACnC;OACS;QAEpB,CACI;IASM;GACT,iBAAiB;GACjB,oBAAoB,EAAE,UAAU,QAAQ;GACxC,iBAAiB,EAAa,EAAK;GACnC,UAAS;EACV,CAAA,GACA,KACC,kBAAA,GAAA,EAAA,UAAA,CACG,KACC,kBAAC,GAAD;GACS;GACC;GACM;EACf,CAAA,GAEF,CACD,EAAA,CAAA,CAED;;AAET"}
|
package/dist/components/SchemaDrivenAnnotationEditor/field/AdditionalPropertiesSchemaField.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdditionalPropertiesSchemaField.js","names":[],"sources":["../../../../src/components/SchemaDrivenAnnotationEditor/field/AdditionalPropertiesSchemaField.tsx"],"sourcesContent":["import {\n FieldProps,\n FormContextType,\n RJSFSchema,\n StrictRJSFSchema,\n} from '@rjsf/utils'\nimport { isEqual } from 'lodash-es'\nimport { useEffect, useState } from 'react'\nimport FullWidthAlert from '../../FullWidthAlert/FullWidthAlert'\nimport { convertToArray } from '../AnnotationEditorUtils'\nimport { GridLegacy as Grid, InputLabel } from '@mui/material'\nimport { AdditionalPropertyContextProvider } from '../template/AdditionalPropertyContext'\n\n// Matches ####-##-##T##:##:##.###Z, e.g. 1970-01-01T12:00:000Z\nconst ISO_TIMESTAMP_REGEX = /\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z/\n\n// Types that correspond to the different input fields that the annotation editor supports\nconst propertyTypeArray = [\n 'String',\n 'Integer',\n 'Float',\n 'Boolean',\n 'Datetime',\n] as const\n\nexport type PropertyType = (typeof propertyTypeArray)[number]\n\nexport function guessPropertyType(list: Array<unknown>): PropertyType {\n if (list.length === 0) {\n // The field was just added, so default to string\n return 'String'\n } else if (\n list.every(\n item => typeof item === 'number' || item === 'NaN', // \"NaN\" is technically a float value\n )\n ) {\n if (list.every(item => Number.isInteger(item))) {\n return 'Integer'\n } else {\n return 'Float'\n }\n } else if (list.every(item => typeof item === 'boolean')) {\n return 'Boolean'\n } else if (\n list.every(item => typeof item === 'string') &&\n list.every((item: string) => !!ISO_TIMESTAMP_REGEX.exec(item))\n ) {\n return 'Datetime'\n }\n // otherwise, default type is 'string'\n return 'String'\n}\n\nexport function transformDataFromPropertyType(\n list: Array<any>,\n propertyType: PropertyType,\n) {\n switch (propertyType) {\n case 'Integer':\n return list.map(item =>\n Number.isNaN(Number(item)) ? 0 : Math.floor(Number(item)),\n )\n\n case 'Float':\n return list.map(item => {\n const asFloat = parseFloat(item as string)\n if (Number.isNaN(asFloat)) {\n return 'NaN'\n } else if (Number.isInteger(asFloat)) {\n return asFloat.toFixed(1)\n } else {\n return asFloat\n }\n })\n case 'Datetime':\n return list.map(item => {\n if (typeof item === 'string' && ISO_TIMESTAMP_REGEX.exec(item)) {\n return item\n } else {\n return new Date().toISOString()\n }\n })\n case 'Boolean':\n return list.map(item => !!item)\n case 'String':\n default:\n return list.map(item => String(item))\n }\n}\n\n/**\n * Maps a Synapse Annotation PropertyType to a JSON Schema that captures the type and format.\n * @param propertyType\n */\nexport function getSchemaForPropertyType(propertyType: PropertyType) {\n switch (propertyType) {\n case 'Datetime':\n return { type: 'string', format: 'datetime' }\n case 'Boolean':\n return { type: 'boolean' }\n case 'Float':\n return { type: 'number' }\n case 'Integer':\n return { type: 'integer' }\n case 'String':\n default:\n return { type: 'string' }\n }\n}\n\n/**\n * react-jsonschema-form SchemaField override for \"additionalProperties\" only. In Synapse these are \"custom annotations\".\n * Modifies the data to provide full compatibility with Synapse annotations features.\n *\n * This component provides these enhancements to the SchemaField:\n * - Supports selecting a type, and changing the input widget appropriately\n * - Identifying the type on mount\n * - Treat all field values as arrays\n * - When the last array value is removed, remove the entire key from the form.\n * @param props\n * @returns\n */\nexport function AdditionalPropertiesSchemaField<\n T extends Array<unknown> = Array<unknown>,\n S extends StrictRJSFSchema = RJSFSchema,\n F extends FormContextType = any,\n>(\n props: FieldProps<T, S, F> & {\n onDropPropertyClick: (key: string) => (event: any) => void\n },\n) {\n const {\n formData,\n onChange,\n registry,\n schema,\n name,\n onDropPropertyClick,\n idSchema,\n } = props\n const id = idSchema.$id\n const { ArrayField } = registry.fields\n const { SelectWidget } = registry.widgets\n\n // The type determines which widget we show.\n const [propertyType, setPropertyType] = useState(\n guessPropertyType(convertToArray(formData)),\n )\n\n // If the property type is updated, store it in a new variable where we'll show a warning if data may be lost on coersion\n const [nextPropertyType, setNextPropertyType] = useState(propertyType)\n\n /**\n * This effect is invoked whenever the user attempts to change the datatype of a custom annotation.\n */\n useEffect(() => {\n function onNextPropertyTypeUpdate() {\n if (Array.isArray(formData)) {\n const dataIsEmpty =\n formData.length === 0 ||\n formData.every(item => item == null || item == '')\n const coercedList = transformDataFromPropertyType(\n formData,\n nextPropertyType,\n )\n // if the data is empty or identical after conversion, then just update the property type\n if (dataIsEmpty || nextPropertyType !== propertyType) {\n if (isEqual(formData, coercedList)) {\n setPropertyType(nextPropertyType)\n }\n }\n }\n }\n\n onNextPropertyTypeUpdate()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [nextPropertyType])\n\n /**\n * This effect is invoked whenever the propertyType changes.\n */\n useEffect(() => {\n function coerceDataAndUpdateWidget() {\n if (Array.isArray(formData)) {\n if (formData.every(item => item == null)) {\n onDropPropertyClick(name)(new CustomEvent('dropEmptyProperty'))\n } else {\n const coercedList = transformDataFromPropertyType(\n formData,\n nextPropertyType,\n ) as unknown as T\n\n // Data conversion is non-destructive or has been confirmed by the user\n setPropertyType(nextPropertyType)\n // Coerce the data to match the new type\n onChange(coercedList)\n }\n }\n }\n\n coerceDataAndUpdateWidget()\n // Don't add other properties to dependency array because we don't want to automatically coerce input\n // i.e. Only coerce data when the type changes, which should only be on mount or when the user explicitly chooses a new type.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [propertyType])\n\n const itemsSchema = getSchemaForPropertyType(propertyType)\n\n useEffect(() => {\n // If the additional property has not yet been coerced to an array, trigger an update to coerce and render nothing.\n // We must do this because RJSF does not currently batch these kinds of updates\n // See https://github.com/rjsf-team/react-jsonschema-form/issues/3367\n if (!Array.isArray(formData)) {\n onChange(convertToArray(formData) as unknown as T)\n }\n }, [formData, onChange])\n\n if (!Array.isArray(formData)) {\n return <></>\n }\n\n return (\n <AdditionalPropertyContextProvider\n value={{\n dropProperty: e => {\n onDropPropertyClick(name)(e)\n },\n }}\n >\n <Grid item xs={2}>\n <InputLabel htmlFor={`${id}-type`}>Type</InputLabel>\n <SelectWidget\n name={'Type'}\n id={`${id}-type`}\n schema={{} as S}\n options={{\n enumOptions: propertyTypeArray.map(type => ({\n label: type,\n value: type,\n })),\n }}\n value={propertyType}\n onChange={value => {\n setNextPropertyType(value as PropertyType)\n }}\n disabled={props.disabled}\n readOnly={props.readonly}\n required={true}\n isClearable={false}\n onBlur={() => {}}\n onFocus={() => {}}\n registry={registry}\n label={'Type'}\n />\n </Grid>\n <Grid item xs={7}>\n <ArrayField\n {...props}\n uiSchema={{\n ...props.uiSchema,\n 'ui:title': 'Value(s)',\n items: {\n ...props.uiSchema?.items,\n 'ui:placeholder': 'New Value',\n },\n }}\n schema={{\n ...schema,\n items: {\n ...itemsSchema,\n },\n }}\n />\n </Grid>\n {propertyType !== nextPropertyType && (\n <FullWidthAlert\n variant=\"warning\"\n title=\"Data may be lost when converting types\"\n description={`Are you sure you want to convert ${name} from ${propertyType} to ${nextPropertyType}? Current values may be lost on conversion.`}\n primaryButtonConfig={{\n text: 'Convert',\n onClick: () => {\n setPropertyType(nextPropertyType)\n },\n }}\n secondaryButtonConfig={{\n text: 'Cancel',\n onClick: () => {\n setNextPropertyType(propertyType)\n },\n }}\n isGlobal={false}\n />\n )}\n </AdditionalPropertyContextProvider>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdditionalPropertiesSchemaField.js","names":[],"sources":["../../../../src/components/SchemaDrivenAnnotationEditor/field/AdditionalPropertiesSchemaField.tsx"],"sourcesContent":["import {\n FieldProps,\n FormContextType,\n RJSFSchema,\n StrictRJSFSchema,\n} from '@rjsf/utils'\nimport { isEqual } from 'lodash-es'\nimport { useEffect, useState } from 'react'\nimport FullWidthAlert from '../../FullWidthAlert/FullWidthAlert'\nimport { convertToArray } from '../AnnotationEditorUtils'\nimport { GridLegacy as Grid, InputLabel } from '@mui/material'\nimport { AdditionalPropertyContextProvider } from '../template/AdditionalPropertyContext'\n\n// Matches ####-##-##T##:##:##.###Z, e.g. 1970-01-01T12:00:000Z\nconst ISO_TIMESTAMP_REGEX = /\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z/\n\n// Types that correspond to the different input fields that the annotation editor supports\nconst propertyTypeArray = [\n 'String',\n 'Integer',\n 'Float',\n 'Boolean',\n 'Datetime',\n] as const\n\nexport type PropertyType = (typeof propertyTypeArray)[number]\n\nexport function guessPropertyType(list: Array<unknown>): PropertyType {\n if (list.length === 0) {\n // The field was just added, so default to string\n return 'String'\n } else if (\n list.every(\n item => typeof item === 'number' || item === 'NaN', // \"NaN\" is technically a float value\n )\n ) {\n if (list.every(item => Number.isInteger(item))) {\n return 'Integer'\n } else {\n return 'Float'\n }\n } else if (list.every(item => typeof item === 'boolean')) {\n return 'Boolean'\n } else if (\n list.every(item => typeof item === 'string') &&\n list.every((item: string) => !!ISO_TIMESTAMP_REGEX.exec(item))\n ) {\n return 'Datetime'\n }\n // otherwise, default type is 'string'\n return 'String'\n}\n\nexport function transformDataFromPropertyType(\n list: Array<any>,\n propertyType: PropertyType,\n) {\n switch (propertyType) {\n case 'Integer':\n return list.map(item =>\n Number.isNaN(Number(item)) ? 0 : Math.floor(Number(item)),\n )\n\n case 'Float':\n return list.map(item => {\n const asFloat = parseFloat(item as string)\n if (Number.isNaN(asFloat)) {\n return 'NaN'\n } else if (Number.isInteger(asFloat)) {\n return asFloat.toFixed(1)\n } else {\n return asFloat\n }\n })\n case 'Datetime':\n return list.map(item => {\n if (typeof item === 'string' && ISO_TIMESTAMP_REGEX.exec(item)) {\n return item\n } else {\n return new Date().toISOString()\n }\n })\n case 'Boolean':\n return list.map(item => !!item)\n case 'String':\n default:\n return list.map(item => String(item))\n }\n}\n\n/**\n * Maps a Synapse Annotation PropertyType to a JSON Schema that captures the type and format.\n * @param propertyType\n */\nexport function getSchemaForPropertyType(propertyType: PropertyType) {\n switch (propertyType) {\n case 'Datetime':\n return { type: 'string', format: 'datetime' }\n case 'Boolean':\n return { type: 'boolean' }\n case 'Float':\n return { type: 'number' }\n case 'Integer':\n return { type: 'integer' }\n case 'String':\n default:\n return { type: 'string' }\n }\n}\n\n/**\n * react-jsonschema-form SchemaField override for \"additionalProperties\" only. In Synapse these are \"custom annotations\".\n * Modifies the data to provide full compatibility with Synapse annotations features.\n *\n * This component provides these enhancements to the SchemaField:\n * - Supports selecting a type, and changing the input widget appropriately\n * - Identifying the type on mount\n * - Treat all field values as arrays\n * - When the last array value is removed, remove the entire key from the form.\n * @param props\n * @returns\n */\nexport function AdditionalPropertiesSchemaField<\n T extends Array<unknown> = Array<unknown>,\n S extends StrictRJSFSchema = RJSFSchema,\n F extends FormContextType = any,\n>(\n props: FieldProps<T, S, F> & {\n onDropPropertyClick: (key: string) => (event: any) => void\n },\n) {\n const {\n formData,\n onChange,\n registry,\n schema,\n name,\n onDropPropertyClick,\n idSchema,\n } = props\n const id = idSchema.$id\n const { ArrayField } = registry.fields\n const { SelectWidget } = registry.widgets\n\n // The type determines which widget we show.\n const [propertyType, setPropertyType] = useState(\n guessPropertyType(convertToArray(formData)),\n )\n\n // If the property type is updated, store it in a new variable where we'll show a warning if data may be lost on coersion\n const [nextPropertyType, setNextPropertyType] = useState(propertyType)\n\n /**\n * This effect is invoked whenever the user attempts to change the datatype of a custom annotation.\n */\n useEffect(() => {\n function onNextPropertyTypeUpdate() {\n if (Array.isArray(formData)) {\n const dataIsEmpty =\n formData.length === 0 ||\n formData.every(item => item == null || item == '')\n const coercedList = transformDataFromPropertyType(\n formData,\n nextPropertyType,\n )\n // if the data is empty or identical after conversion, then just update the property type\n if (dataIsEmpty || nextPropertyType !== propertyType) {\n if (isEqual(formData, coercedList)) {\n setPropertyType(nextPropertyType)\n }\n }\n }\n }\n\n onNextPropertyTypeUpdate()\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [nextPropertyType])\n\n /**\n * This effect is invoked whenever the propertyType changes.\n */\n useEffect(() => {\n function coerceDataAndUpdateWidget() {\n if (Array.isArray(formData)) {\n if (formData.every(item => item == null)) {\n onDropPropertyClick(name)(new CustomEvent('dropEmptyProperty'))\n } else {\n const coercedList = transformDataFromPropertyType(\n formData,\n nextPropertyType,\n ) as unknown as T\n\n // Data conversion is non-destructive or has been confirmed by the user\n setPropertyType(nextPropertyType)\n // Coerce the data to match the new type\n onChange(coercedList)\n }\n }\n }\n\n coerceDataAndUpdateWidget()\n // Don't add other properties to dependency array because we don't want to automatically coerce input\n // i.e. Only coerce data when the type changes, which should only be on mount or when the user explicitly chooses a new type.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [propertyType])\n\n const itemsSchema = getSchemaForPropertyType(propertyType)\n\n useEffect(() => {\n // If the additional property has not yet been coerced to an array, trigger an update to coerce and render nothing.\n // We must do this because RJSF does not currently batch these kinds of updates\n // See https://github.com/rjsf-team/react-jsonschema-form/issues/3367\n if (!Array.isArray(formData)) {\n onChange(convertToArray(formData) as unknown as T)\n }\n }, [formData, onChange])\n\n if (!Array.isArray(formData)) {\n return <></>\n }\n\n return (\n <AdditionalPropertyContextProvider\n value={{\n dropProperty: e => {\n onDropPropertyClick(name)(e)\n },\n }}\n >\n <Grid item xs={2}>\n <InputLabel htmlFor={`${id}-type`}>Type</InputLabel>\n <SelectWidget\n name={'Type'}\n id={`${id}-type`}\n schema={{} as S}\n options={{\n enumOptions: propertyTypeArray.map(type => ({\n label: type,\n value: type,\n })),\n }}\n value={propertyType}\n onChange={value => {\n setNextPropertyType(value as PropertyType)\n }}\n disabled={props.disabled}\n readOnly={props.readonly}\n required={true}\n isClearable={false}\n onBlur={() => {}}\n onFocus={() => {}}\n registry={registry}\n label={'Type'}\n />\n </Grid>\n <Grid item xs={7}>\n <ArrayField\n {...props}\n uiSchema={{\n ...props.uiSchema,\n 'ui:title': 'Value(s)',\n items: {\n ...props.uiSchema?.items,\n 'ui:placeholder': 'New Value',\n },\n }}\n schema={{\n ...schema,\n items: {\n ...itemsSchema,\n },\n }}\n />\n </Grid>\n {propertyType !== nextPropertyType && (\n <FullWidthAlert\n variant=\"warning\"\n title=\"Data may be lost when converting types\"\n description={`Are you sure you want to convert ${name} from ${propertyType} to ${nextPropertyType}? Current values may be lost on conversion.`}\n primaryButtonConfig={{\n text: 'Convert',\n onClick: () => {\n setPropertyType(nextPropertyType)\n },\n }}\n secondaryButtonConfig={{\n text: 'Cancel',\n onClick: () => {\n setNextPropertyType(propertyType)\n },\n }}\n isGlobal={false}\n />\n )}\n </AdditionalPropertyContextProvider>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchQueryWrapperPlotNav.d.ts","sourceRoot":"","sources":["../../../src/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAEL,8BAA8B,EAC/B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAA;AACtF,OAAO,EAEL,uBAAuB,EACxB,MAAM,0CAA0C,CAAA;AAIjD,KAAK,iCAAiC,GAAG;IACvC,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2HAA2H;IAC3H,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,kBAAkB,CAAC,EAAE,yBAAyB,CAAA;IAC9C,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,eAAe,CAAC,EAAE,wBAAwB,CAAC,iBAAiB,CAAC,CAAA;IAC7D,4BAA4B,CAAC,EAAE,wBAAwB,CAAC,8BAA8B,CAAC,CAAA;IACvF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAI/B,CAAA;AAED,MAAM,MAAM,8BAA8B,GAAG,iCAAiC,GAC5E,mBAAmB,GACnB,IAAI,CACF,8BAA8B,EAC5B,kBAAkB,GAClB,oBAAoB,GACpB,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,UAAU,GACV,mBAAmB,GACnB,0BAA0B,GAC1B,iBAAiB,GACjB,mBAAmB,GACnB,qBAAqB,GACrB,2BAA2B,GAC3B,gCAAgC,GAChC,sBAAsB,GACtB,sBAAsB,CACzB,GACD,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,gBAAgB,CAAC,GAAG;IACvD,6GAA6G;IAC7G,gBAAgB,CAAC,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAA;IAC9D,8FAA8F;IAC9F,yBAAyB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAChF,+EAA+E;IAC/E,cAAc,CAAC,EAAE,uBAAuB,CAAC,gBAAgB,CAAC,CAAA;IAC1D,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,uBAAuB,CAAC,mBAAmB,CAAC,CAAA;CACjE,CAAA;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAAC,MAAM,EAAE;IACvD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd,GAAG,MAAM,IAAI,8BAA8B,CAE3C;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAC/C,KAAK,EAAE,8BAA8B,
|
|
1
|
+
{"version":3,"file":"SearchQueryWrapperPlotNav.d.ts","sourceRoot":"","sources":["../../../src/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAEL,8BAA8B,EAC/B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAA;AACtF,OAAO,EAEL,uBAAuB,EACxB,MAAM,0CAA0C,CAAA;AAIjD,KAAK,iCAAiC,GAAG;IACvC,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2HAA2H;IAC3H,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,kBAAkB,CAAC,EAAE,yBAAyB,CAAA;IAC9C,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,eAAe,CAAC,EAAE,wBAAwB,CAAC,iBAAiB,CAAC,CAAA;IAC7D,4BAA4B,CAAC,EAAE,wBAAwB,CAAC,8BAA8B,CAAC,CAAA;IACvF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAI/B,CAAA;AAED,MAAM,MAAM,8BAA8B,GAAG,iCAAiC,GAC5E,mBAAmB,GACnB,IAAI,CACF,8BAA8B,EAC5B,kBAAkB,GAClB,oBAAoB,GACpB,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,UAAU,GACV,mBAAmB,GACnB,0BAA0B,GAC1B,iBAAiB,GACjB,mBAAmB,GACnB,qBAAqB,GACrB,2BAA2B,GAC3B,gCAAgC,GAChC,sBAAsB,GACtB,sBAAsB,CACzB,GACD,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,gBAAgB,CAAC,GAAG;IACvD,6GAA6G;IAC7G,gBAAgB,CAAC,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAA;IAC9D,8FAA8F;IAC9F,yBAAyB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAChF,+EAA+E;IAC/E,cAAc,CAAC,EAAE,uBAAuB,CAAC,gBAAgB,CAAC,CAAA;IAC1D,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,uBAAuB,CAAC,mBAAmB,CAAC,CAAA;CACjE,CAAA;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAAC,MAAM,EAAE;IACvD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd,GAAG,MAAM,IAAI,8BAA8B,CAE3C;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAC/C,KAAK,EAAE,8BAA8B,2CA6GtC"}
|
|
@@ -61,6 +61,7 @@ function d(u) {
|
|
|
61
61
|
hideSqlEditorControl: !0,
|
|
62
62
|
hideVisualizationsControl: u.hideVisualizationsControl,
|
|
63
63
|
initialLimit: u.initialLimit,
|
|
64
|
+
customPlots: u.customPlots,
|
|
64
65
|
initialPlotTypeByFacetColumnName: u.initialPlotTypeByFacetColumnName,
|
|
65
66
|
hideTopLevelControls: u.hideTopLevelControls,
|
|
66
67
|
isFullTextSearchEnabled: !0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchQueryWrapperPlotNav.js","names":[],"sources":["../../../src/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.tsx"],"sourcesContent":["import { NoContentPlaceholderType } from '@/components/SynapseTable/NoContentPlaceholderType'\nimport { DEFAULT_PAGE_SIZE } from '@/utils/SynapseConstants'\nimport { Suspense, useRef } from 'react'\nimport { useDeepCompareMemoize } from 'use-deep-compare-effect'\nimport { CardConfiguration } from '../CardContainer/CardConfiguration'\nimport {\n QueryVisualizationWrapper,\n QueryVisualizationWrapperProps,\n} from '../QueryVisualizationWrapper'\nimport QueryWrapperLoadingScreen from '../QueryWrapper/QueryWrapperLoadingScreen'\nimport { SynapseTableConfiguration } from '../SynapseTable'\nimport { TopLevelControlsProps } from '../SynapseTable/TopLevelControls/TopLevelControls'\nimport { PlotsContainerProps } from '../widgets/facet-nav/PlotsContainer'\nimport { FacetFilterControlsProps } from '../widgets/query-filter/FacetFilterControls'\nimport {\n SearchQueryWrapper,\n SearchQueryWrapperProps,\n} from '../SearchQueryWrapper/SearchQueryWrapper'\nimport { QueryWrapperPlotNavContents } from '../QueryWrapperPlotNav/QueryWrapperPlotNav'\nimport { useGetSuggestionsForSearchIndex } from '../SearchQueryWrapper/SearchQueryUseQueryOptions'\n\ntype SearchQueryWrapperPlotNavOwnProps = {\n /** The ID of the SearchIndex entity to query. */\n searchIndexId: string\n /**\n * The name of the field to use for autocomplete suggestions.\n * When provided, the search bar will show a dropdown of suggestions as the user types,\n * populated by querying the search index with this field restricted via POST /search/autocomplete.\n */\n autocompleteFieldName?: string\n /** Whether the displayed results should be paginated or infinite. Default for cards is true, default for table is false */\n isInfinite?: boolean\n tableConfiguration?: SynapseTableConfiguration\n cardConfiguration?: CardConfiguration\n facetsToPlot?: string[]\n availableFacets?: FacetFilterControlsProps['availableFacets']\n initialExpandedFacetControls?: FacetFilterControlsProps['initialExpandedFacetControls']\n initialLimit?: number\n hideTopLevelControls?: boolean\n // Note: hideDownload, hideSqlEditorControl are intentionally\n // excluded — they are hardcoded to true because these controls are not applicable to the\n // SearchQueryServicesApi (no file download, no SQL editing).\n}\n\nexport type SearchQueryWrapperPlotNavProps = SearchQueryWrapperPlotNavOwnProps &\n PlotsContainerProps &\n Pick<\n QueryVisualizationWrapperProps,\n | 'defaultShowPlots'\n | 'visibleColumnCount'\n | 'hiddenColumns'\n | 'columnAliases'\n | 'renderFacetValue'\n | 'rgbIndex'\n | 'showLastUpdatedOn'\n | 'noContentPlaceholderType'\n | 'unitDescription'\n | 'helpConfiguration'\n | 'hideCopyToClipboard'\n | 'hideVisualizationsControl'\n | 'lockTextMatchesQueryFilterPill'\n | 'hideSearchBarControl'\n | 'defaultShowSearchBar'\n > &\n Pick<TopLevelControlsProps, 'name' | 'hideQueryCount'> & {\n /** Optional initial query parameters. Only selectedFacets, additionalFilters, limit, and offset are used. */\n initQueryRequest?: SearchQueryWrapperProps['initQueryRequest']\n /** Called when the query result bundle changes (e.g. to report result counts to a parent). */\n onQueryResultBundleChange?: SearchQueryWrapperProps['onQueryResultBundleChange']\n /** Whether the URL should update when the query is modified (deep linking). */\n shouldDeepLink?: SearchQueryWrapperProps['shouldDeepLink']\n /** OpenSearch query strategy and field-boost configuration. Forwarded to SearchQueryWrapper. */\n searchQueryConfig?: SearchQueryWrapperProps['searchQueryConfig']\n }\n\n/**\n * Type guard that identifies a SearchQueryWrapperPlotNavProps from the union\n * `QueryWrapperPlotNavProps | SearchQueryWrapperPlotNavProps`.\n *\n * Two conditions are checked to be robust against future API changes:\n * 1. The config has `searchIndexId` (present only on SearchQueryWrapperPlotNavProps).\n * 2. The config does NOT have `sql` (always present on QueryWrapperPlotNavProps via\n * QueryOrDeprecatedSearchParams), so that if QueryWrapperPlotNavProps ever gains a\n * `searchIndexId` field the guard remains correct.\n */\nexport function isSearchQueryWrapperPlotNavProps(config: {\n searchIndexId?: unknown\n sql?: unknown\n}): config is SearchQueryWrapperPlotNavProps {\n return 'searchIndexId' in config && !('sql' in config)\n}\n\n/**\n * A component similar to QueryWrapperPlotNav that sources its data from the SearchQueryServicesApi\n * rather than the standard Synapse Table Services. Facets, table/card rendering, and plots are\n * all reused from the existing component library.\n */\nexport default function SearchQueryWrapperPlotNav(\n props: SearchQueryWrapperPlotNavProps,\n) {\n const {\n tableConfiguration,\n isInfinite = !tableConfiguration,\n unitDescription,\n helpConfiguration,\n initQueryRequest: initQueryRequestFromProps,\n onQueryResultBundleChange,\n searchIndexId,\n autocompleteFieldName,\n shouldDeepLink,\n } = props\n\n const getSuggestions = useGetSuggestionsForSearchIndex(\n searchIndexId,\n autocompleteFieldName,\n )\n\n const initQueryRequest: SearchQueryWrapperProps['initQueryRequest'] =\n useDeepCompareMemoize({\n partMask: initQueryRequestFromProps?.partMask ?? 0,\n query: {\n selectedFacets: initQueryRequestFromProps?.query?.selectedFacets,\n additionalFilters: initQueryRequestFromProps?.query?.additionalFilters,\n sort: initQueryRequestFromProps?.query?.sort,\n limit: initQueryRequestFromProps?.query?.limit ?? DEFAULT_PAGE_SIZE,\n offset: initQueryRequestFromProps?.query?.offset ?? 0,\n },\n })\n\n // Remount SearchQueryWrapper when initQueryRequest content changes.\n // useDeepCompareMemoize returns the same reference when content is equal, so reference\n // comparison is sufficient here. A counter is used rather than JSON.stringify to avoid\n // serializing the object on every render.\n const mountKeyRef = useRef(0)\n const prevInitQueryRef = useRef(initQueryRequest)\n if (prevInitQueryRef.current !== initQueryRequest) {\n mountKeyRef.current += 1\n prevInitQueryRef.current = initQueryRequest\n }\n\n return (\n <SearchQueryWrapper\n key={mountKeyRef.current}\n searchIndexId={searchIndexId}\n initQueryRequest={initQueryRequest}\n isInfinite={isInfinite}\n onQueryResultBundleChange={onQueryResultBundleChange}\n shouldDeepLink={shouldDeepLink}\n searchQueryConfig={props.searchQueryConfig}\n >\n <Suspense fallback={<QueryWrapperLoadingScreen />}>\n <QueryVisualizationWrapper\n unitDescription={unitDescription}\n rgbIndex={props.rgbIndex}\n columnAliases={props.columnAliases}\n renderFacetValue={props.renderFacetValue}\n dataUseModifiersColumnName={\n props.cardConfiguration?.genericCardSchema\n ?.dataUseModifiersColumnName\n }\n helpConfiguration={helpConfiguration}\n visibleColumnCount={props.visibleColumnCount}\n hiddenColumns={props.hiddenColumns}\n defaultShowPlots={props.defaultShowPlots}\n hideCopyToClipboard={props.hideCopyToClipboard}\n hideSearchBarControl={props.hideSearchBarControl ?? false}\n lockTextMatchesQueryFilterPill={props.lockTextMatchesQueryFilterPill}\n defaultShowSearchBar={props.defaultShowSearchBar ?? true}\n showLastUpdatedOn={props.showLastUpdatedOn}\n noContentPlaceholderType={\n props.noContentPlaceholderType ??\n NoContentPlaceholderType.INTERACTIVE\n }\n >\n <QueryWrapperPlotNavContents\n tableConfiguration={tableConfiguration}\n name={props.name}\n cardConfiguration={props.cardConfiguration}\n facetsToPlot={props.facetsToPlot}\n availableFacets={props.availableFacets}\n initialExpandedFacetControls={props.initialExpandedFacetControls}\n hideDownload={true}\n hideQueryCount={props.hideQueryCount ?? false}\n hideSqlEditorControl={true}\n hideVisualizationsControl={props.hideVisualizationsControl}\n initialLimit={props.initialLimit}\n initialPlotTypeByFacetColumnName={\n props.initialPlotTypeByFacetColumnName\n }\n hideTopLevelControls={props.hideTopLevelControls}\n isFullTextSearchEnabled={true}\n searchConfiguration={{\n ftsConfig: {\n textMatchesMode: 'NATURAL_LANGUAGE',\n minSearchQueryLength: 1,\n replaceExistingFilter: true,\n getSuggestions: autocompleteFieldName\n ? getSuggestions\n : undefined,\n },\n }}\n />\n </QueryVisualizationWrapper>\n </Suspense>\n </SearchQueryWrapper>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAqFA,SAAgB,EAAiC,GAGJ;CAC3C,OAAO,mBAAmB,KAAU,EAAE,SAAS;AACjD;AAOA,SAAwB,EACtB,GACA;CACA,IAAM,EACJ,uBACA,gBAAa,CAAC,GACd,oBACA,sBACA,kBAAkB,GAClB,8BACA,kBACA,0BACA,sBACE,GAEE,IAAiB,EACrB,GACA,CACF,GAEM,IACJ,EAAsB;EACpB,UAAU,GAA2B,YAAY;EACjD,OAAO;GACL,gBAAgB,GAA2B,OAAO;GAClD,mBAAmB,GAA2B,OAAO;GACrD,MAAM,GAA2B,OAAO;GACxC,OAAO,GAA2B,OAAO,SAAA;GACzC,QAAQ,GAA2B,OAAO,UAAU;EACtD;CACF,CAAC,GAMG,IAAc,EAAO,CAAC,GACtB,IAAmB,EAAO,CAAgB;CAMhD,OALI,EAAiB,YAAY,MAC/B,EAAY,WAAW,GACvB,EAAiB,UAAU,IAI3B,kBAAC,GAAD;EAEiB;EACG;EACN;EACe;EACX;EAChB,mBAAmB,EAAM;YAEzB,kBAAC,GAAD;GAAU,UAAU,kBAAC,GAAD,CAA4B,CAAA;aAC9C,kBAAC,GAAD;IACmB;IACjB,UAAU,EAAM;IAChB,eAAe,EAAM;IACrB,kBAAkB,EAAM;IACxB,4BACE,EAAM,mBAAmB,mBACrB;IAEa;IACnB,oBAAoB,EAAM;IAC1B,eAAe,EAAM;IACrB,kBAAkB,EAAM;IACxB,qBAAqB,EAAM;IAC3B,sBAAsB,EAAM,wBAAwB;IACpD,gCAAgC,EAAM;IACtC,sBAAsB,EAAM,wBAAwB;IACpD,mBAAmB,EAAM;IACzB,0BACE,EAAM,4BACN,EAAyB;cAG3B,kBAAC,GAAD;KACsB;KACpB,MAAM,EAAM;KACZ,mBAAmB,EAAM;KACzB,cAAc,EAAM;KACpB,iBAAiB,EAAM;KACvB,8BAA8B,EAAM;KACpC,cAAc;KACd,gBAAgB,EAAM,kBAAkB;KACxC,sBAAsB;KACtB,2BAA2B,EAAM;KACjC,cAAc,EAAM;KACpB,kCACE,EAAM;KAER,sBAAsB,EAAM;KAC5B,yBAAyB;KACzB,qBAAqB,EACnB,WAAW;MACT,iBAAiB;MACjB,sBAAsB;MACtB,uBAAuB;MACvB,gBAAgB,IACZ,IACA,KAAA;KACN,EACF;IACD,CAAA;GACwB,CAAA;EACnB,CAAA;CACQ,GA9Db,EAAY,OA8DC;AAExB"}
|
|
1
|
+
{"version":3,"file":"SearchQueryWrapperPlotNav.js","names":[],"sources":["../../../src/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.tsx"],"sourcesContent":["import { NoContentPlaceholderType } from '@/components/SynapseTable/NoContentPlaceholderType'\nimport { DEFAULT_PAGE_SIZE } from '@/utils/SynapseConstants'\nimport { Suspense, useRef } from 'react'\nimport { useDeepCompareMemoize } from 'use-deep-compare-effect'\nimport { CardConfiguration } from '../CardContainer/CardConfiguration'\nimport {\n QueryVisualizationWrapper,\n QueryVisualizationWrapperProps,\n} from '../QueryVisualizationWrapper'\nimport QueryWrapperLoadingScreen from '../QueryWrapper/QueryWrapperLoadingScreen'\nimport { SynapseTableConfiguration } from '../SynapseTable'\nimport { TopLevelControlsProps } from '../SynapseTable/TopLevelControls/TopLevelControls'\nimport { PlotsContainerProps } from '../widgets/facet-nav/PlotsContainer'\nimport { FacetFilterControlsProps } from '../widgets/query-filter/FacetFilterControls'\nimport {\n SearchQueryWrapper,\n SearchQueryWrapperProps,\n} from '../SearchQueryWrapper/SearchQueryWrapper'\nimport { QueryWrapperPlotNavContents } from '../QueryWrapperPlotNav/QueryWrapperPlotNav'\nimport { useGetSuggestionsForSearchIndex } from '../SearchQueryWrapper/SearchQueryUseQueryOptions'\n\ntype SearchQueryWrapperPlotNavOwnProps = {\n /** The ID of the SearchIndex entity to query. */\n searchIndexId: string\n /**\n * The name of the field to use for autocomplete suggestions.\n * When provided, the search bar will show a dropdown of suggestions as the user types,\n * populated by querying the search index with this field restricted via POST /search/autocomplete.\n */\n autocompleteFieldName?: string\n /** Whether the displayed results should be paginated or infinite. Default for cards is true, default for table is false */\n isInfinite?: boolean\n tableConfiguration?: SynapseTableConfiguration\n cardConfiguration?: CardConfiguration\n facetsToPlot?: string[]\n availableFacets?: FacetFilterControlsProps['availableFacets']\n initialExpandedFacetControls?: FacetFilterControlsProps['initialExpandedFacetControls']\n initialLimit?: number\n hideTopLevelControls?: boolean\n // Note: hideDownload, hideSqlEditorControl are intentionally\n // excluded — they are hardcoded to true because these controls are not applicable to the\n // SearchQueryServicesApi (no file download, no SQL editing).\n}\n\nexport type SearchQueryWrapperPlotNavProps = SearchQueryWrapperPlotNavOwnProps &\n PlotsContainerProps &\n Pick<\n QueryVisualizationWrapperProps,\n | 'defaultShowPlots'\n | 'visibleColumnCount'\n | 'hiddenColumns'\n | 'columnAliases'\n | 'renderFacetValue'\n | 'rgbIndex'\n | 'showLastUpdatedOn'\n | 'noContentPlaceholderType'\n | 'unitDescription'\n | 'helpConfiguration'\n | 'hideCopyToClipboard'\n | 'hideVisualizationsControl'\n | 'lockTextMatchesQueryFilterPill'\n | 'hideSearchBarControl'\n | 'defaultShowSearchBar'\n > &\n Pick<TopLevelControlsProps, 'name' | 'hideQueryCount'> & {\n /** Optional initial query parameters. Only selectedFacets, additionalFilters, limit, and offset are used. */\n initQueryRequest?: SearchQueryWrapperProps['initQueryRequest']\n /** Called when the query result bundle changes (e.g. to report result counts to a parent). */\n onQueryResultBundleChange?: SearchQueryWrapperProps['onQueryResultBundleChange']\n /** Whether the URL should update when the query is modified (deep linking). */\n shouldDeepLink?: SearchQueryWrapperProps['shouldDeepLink']\n /** OpenSearch query strategy and field-boost configuration. Forwarded to SearchQueryWrapper. */\n searchQueryConfig?: SearchQueryWrapperProps['searchQueryConfig']\n }\n\n/**\n * Type guard that identifies a SearchQueryWrapperPlotNavProps from the union\n * `QueryWrapperPlotNavProps | SearchQueryWrapperPlotNavProps`.\n *\n * Two conditions are checked to be robust against future API changes:\n * 1. The config has `searchIndexId` (present only on SearchQueryWrapperPlotNavProps).\n * 2. The config does NOT have `sql` (always present on QueryWrapperPlotNavProps via\n * QueryOrDeprecatedSearchParams), so that if QueryWrapperPlotNavProps ever gains a\n * `searchIndexId` field the guard remains correct.\n */\nexport function isSearchQueryWrapperPlotNavProps(config: {\n searchIndexId?: unknown\n sql?: unknown\n}): config is SearchQueryWrapperPlotNavProps {\n return 'searchIndexId' in config && !('sql' in config)\n}\n\n/**\n * A component similar to QueryWrapperPlotNav that sources its data from the SearchQueryServicesApi\n * rather than the standard Synapse Table Services. Facets, table/card rendering, and plots are\n * all reused from the existing component library.\n */\nexport default function SearchQueryWrapperPlotNav(\n props: SearchQueryWrapperPlotNavProps,\n) {\n const {\n tableConfiguration,\n isInfinite = !tableConfiguration,\n unitDescription,\n helpConfiguration,\n initQueryRequest: initQueryRequestFromProps,\n onQueryResultBundleChange,\n searchIndexId,\n autocompleteFieldName,\n shouldDeepLink,\n } = props\n\n const getSuggestions = useGetSuggestionsForSearchIndex(\n searchIndexId,\n autocompleteFieldName,\n )\n\n const initQueryRequest: SearchQueryWrapperProps['initQueryRequest'] =\n useDeepCompareMemoize({\n partMask: initQueryRequestFromProps?.partMask ?? 0,\n query: {\n selectedFacets: initQueryRequestFromProps?.query?.selectedFacets,\n additionalFilters: initQueryRequestFromProps?.query?.additionalFilters,\n sort: initQueryRequestFromProps?.query?.sort,\n limit: initQueryRequestFromProps?.query?.limit ?? DEFAULT_PAGE_SIZE,\n offset: initQueryRequestFromProps?.query?.offset ?? 0,\n },\n })\n\n // Remount SearchQueryWrapper when initQueryRequest content changes.\n // useDeepCompareMemoize returns the same reference when content is equal, so reference\n // comparison is sufficient here. A counter is used rather than JSON.stringify to avoid\n // serializing the object on every render.\n const mountKeyRef = useRef(0)\n const prevInitQueryRef = useRef(initQueryRequest)\n if (prevInitQueryRef.current !== initQueryRequest) {\n mountKeyRef.current += 1\n prevInitQueryRef.current = initQueryRequest\n }\n\n return (\n <SearchQueryWrapper\n key={mountKeyRef.current}\n searchIndexId={searchIndexId}\n initQueryRequest={initQueryRequest}\n isInfinite={isInfinite}\n onQueryResultBundleChange={onQueryResultBundleChange}\n shouldDeepLink={shouldDeepLink}\n searchQueryConfig={props.searchQueryConfig}\n >\n <Suspense fallback={<QueryWrapperLoadingScreen />}>\n <QueryVisualizationWrapper\n unitDescription={unitDescription}\n rgbIndex={props.rgbIndex}\n columnAliases={props.columnAliases}\n renderFacetValue={props.renderFacetValue}\n dataUseModifiersColumnName={\n props.cardConfiguration?.genericCardSchema\n ?.dataUseModifiersColumnName\n }\n helpConfiguration={helpConfiguration}\n visibleColumnCount={props.visibleColumnCount}\n hiddenColumns={props.hiddenColumns}\n defaultShowPlots={props.defaultShowPlots}\n hideCopyToClipboard={props.hideCopyToClipboard}\n hideSearchBarControl={props.hideSearchBarControl ?? false}\n lockTextMatchesQueryFilterPill={props.lockTextMatchesQueryFilterPill}\n defaultShowSearchBar={props.defaultShowSearchBar ?? true}\n showLastUpdatedOn={props.showLastUpdatedOn}\n noContentPlaceholderType={\n props.noContentPlaceholderType ??\n NoContentPlaceholderType.INTERACTIVE\n }\n >\n <QueryWrapperPlotNavContents\n tableConfiguration={tableConfiguration}\n name={props.name}\n cardConfiguration={props.cardConfiguration}\n facetsToPlot={props.facetsToPlot}\n availableFacets={props.availableFacets}\n initialExpandedFacetControls={props.initialExpandedFacetControls}\n hideDownload={true}\n hideQueryCount={props.hideQueryCount ?? false}\n hideSqlEditorControl={true}\n hideVisualizationsControl={props.hideVisualizationsControl}\n initialLimit={props.initialLimit}\n customPlots={props.customPlots}\n initialPlotTypeByFacetColumnName={\n props.initialPlotTypeByFacetColumnName\n }\n hideTopLevelControls={props.hideTopLevelControls}\n isFullTextSearchEnabled={true}\n searchConfiguration={{\n ftsConfig: {\n textMatchesMode: 'NATURAL_LANGUAGE',\n minSearchQueryLength: 1,\n replaceExistingFilter: true,\n getSuggestions: autocompleteFieldName\n ? getSuggestions\n : undefined,\n },\n }}\n />\n </QueryVisualizationWrapper>\n </Suspense>\n </SearchQueryWrapper>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAqFA,SAAgB,EAAiC,GAGJ;CAC3C,OAAO,mBAAmB,KAAU,EAAE,SAAS;AACjD;AAOA,SAAwB,EACtB,GACA;CACA,IAAM,EACJ,uBACA,gBAAa,CAAC,GACd,oBACA,sBACA,kBAAkB,GAClB,8BACA,kBACA,0BACA,sBACE,GAEE,IAAiB,EACrB,GACA,CACF,GAEM,IACJ,EAAsB;EACpB,UAAU,GAA2B,YAAY;EACjD,OAAO;GACL,gBAAgB,GAA2B,OAAO;GAClD,mBAAmB,GAA2B,OAAO;GACrD,MAAM,GAA2B,OAAO;GACxC,OAAO,GAA2B,OAAO,SAAA;GACzC,QAAQ,GAA2B,OAAO,UAAU;EACtD;CACF,CAAC,GAMG,IAAc,EAAO,CAAC,GACtB,IAAmB,EAAO,CAAgB;CAMhD,OALI,EAAiB,YAAY,MAC/B,EAAY,WAAW,GACvB,EAAiB,UAAU,IAI3B,kBAAC,GAAD;EAEiB;EACG;EACN;EACe;EACX;EAChB,mBAAmB,EAAM;YAEzB,kBAAC,GAAD;GAAU,UAAU,kBAAC,GAAD,CAA4B,CAAA;aAC9C,kBAAC,GAAD;IACmB;IACjB,UAAU,EAAM;IAChB,eAAe,EAAM;IACrB,kBAAkB,EAAM;IACxB,4BACE,EAAM,mBAAmB,mBACrB;IAEa;IACnB,oBAAoB,EAAM;IAC1B,eAAe,EAAM;IACrB,kBAAkB,EAAM;IACxB,qBAAqB,EAAM;IAC3B,sBAAsB,EAAM,wBAAwB;IACpD,gCAAgC,EAAM;IACtC,sBAAsB,EAAM,wBAAwB;IACpD,mBAAmB,EAAM;IACzB,0BACE,EAAM,4BACN,EAAyB;cAG3B,kBAAC,GAAD;KACsB;KACpB,MAAM,EAAM;KACZ,mBAAmB,EAAM;KACzB,cAAc,EAAM;KACpB,iBAAiB,EAAM;KACvB,8BAA8B,EAAM;KACpC,cAAc;KACd,gBAAgB,EAAM,kBAAkB;KACxC,sBAAsB;KACtB,2BAA2B,EAAM;KACjC,cAAc,EAAM;KACpB,aAAa,EAAM;KACnB,kCACE,EAAM;KAER,sBAAsB,EAAM;KAC5B,yBAAyB;KACzB,qBAAqB,EACnB,WAAW;MACT,iBAAiB;MACjB,sBAAsB;MACtB,uBAAuB;MACvB,gBAAgB,IACZ,IACA,KAAA;KACN,EACF;IACD,CAAA;GACwB,CAAA;EACnB,CAAA;CACQ,GA/Db,EAAY,OA+DC;AAExB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorybookComponentWrapper.js","names":[],"sources":["../../src/components/StorybookComponentWrapper.tsx"],"sourcesContent":["import { StandaloneLoginForm } from '@/components/Authentication/index'\nimport SynapseClient from '@/synapse-client'\nimport {\n defaultQueryClientConfig,\n SynapseContextProvider,\n SynapseContextType,\n} from '@/utils'\nimport { ApplicationSessionContextProvider } from '@/utils/AppUtils/session/ApplicationSessionContext'\nimport {\n SynapseSessionManager,\n type SessionState,\n} from '@/utils/AppUtils/session/SynapseSessionManager'\nimport { STACK_MAP, SynapseStack } from '@/utils/functions/getEndpoint'\nimport useDetectSSOCode from '@/utils/hooks/useDetectSSOCode'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { ReactQueryDevtools } from '@tanstack/react-query-devtools'\nimport {\n ReactNode,\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react'\nimport { createMemoryRouter } from 'react-router'\nimport { RouterProvider } from 'react-router/dom'\nimport { SynapseToastContainer } from './ToastMessage'\n\nconst storybookQueryClient = new QueryClient(defaultQueryClientConfig)\n\n// Module-level session manager instance, stable across story switches (like the QueryClient above)\nconst sessionManager = new SynapseSessionManager({\n onSessionInvalid: () => {\n // In Storybook, just refresh the session instead of reloading the page\n sessionManager.refreshSession()\n },\n})\n\nfunction overrideEndpoint(stack: SynapseStack) {\n const endpointConfig = STACK_MAP[stack]\n ;(window as any)['SRC_OVERRIDE_ENDPOINT_CONFIG'] = endpointConfig\n}\n\n/**\n * Wraps storybook story components to ensure that all components receive required context.\n * @param props\n * @returns\n */\nexport function StorybookComponentWrapper(props: {\n children: ReactNode\n /* This will match the `globalTypes` object in preview.tsx. */\n storybookContext: {\n args: {\n isAuthenticated?: boolean\n }\n globals: {\n stack?: SynapseStack\n showReactQueryDevtools?: boolean\n }\n parameters: {\n stack?: SynapseStack\n withRouter?: boolean\n requireLogin?: boolean\n }\n }\n}) {\n const { storybookContext } = props\n\n const currentStack: SynapseStack = (storybookContext.globals.stack ||\n storybookContext.parameters.stack) as SynapseStack\n\n useEffect(() => {\n overrideEndpoint(currentStack)\n }, [currentStack])\n\n // Subscribe to the framework-agnostic SynapseSessionManager for token/auth state\n // These methods are bound in the SynapseSessionManager constructor, so they are safe to pass directly.\n const sessionState: SessionState = useSyncExternalStore(\n //
|
|
1
|
+
{"version":3,"file":"StorybookComponentWrapper.js","names":[],"sources":["../../src/components/StorybookComponentWrapper.tsx"],"sourcesContent":["import { StandaloneLoginForm } from '@/components/Authentication/index'\nimport SynapseClient from '@/synapse-client'\nimport {\n defaultQueryClientConfig,\n SynapseContextProvider,\n SynapseContextType,\n} from '@/utils'\nimport { ApplicationSessionContextProvider } from '@/utils/AppUtils/session/ApplicationSessionContext'\nimport {\n SynapseSessionManager,\n type SessionState,\n} from '@/utils/AppUtils/session/SynapseSessionManager'\nimport { STACK_MAP, SynapseStack } from '@/utils/functions/getEndpoint'\nimport useDetectSSOCode from '@/utils/hooks/useDetectSSOCode'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { ReactQueryDevtools } from '@tanstack/react-query-devtools'\nimport {\n ReactNode,\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react'\nimport { createMemoryRouter } from 'react-router'\nimport { RouterProvider } from 'react-router/dom'\nimport { SynapseToastContainer } from './ToastMessage'\n\nconst storybookQueryClient = new QueryClient(defaultQueryClientConfig)\n\n// Module-level session manager instance, stable across story switches (like the QueryClient above)\nconst sessionManager = new SynapseSessionManager({\n onSessionInvalid: () => {\n // In Storybook, just refresh the session instead of reloading the page\n sessionManager.refreshSession()\n },\n})\n\nfunction overrideEndpoint(stack: SynapseStack) {\n const endpointConfig = STACK_MAP[stack]\n ;(window as any)['SRC_OVERRIDE_ENDPOINT_CONFIG'] = endpointConfig\n}\n\n/**\n * Wraps storybook story components to ensure that all components receive required context.\n * @param props\n * @returns\n */\nexport function StorybookComponentWrapper(props: {\n children: ReactNode\n /* This will match the `globalTypes` object in preview.tsx. */\n storybookContext: {\n args: {\n isAuthenticated?: boolean\n }\n globals: {\n stack?: SynapseStack\n showReactQueryDevtools?: boolean\n }\n parameters: {\n stack?: SynapseStack\n withRouter?: boolean\n requireLogin?: boolean\n }\n }\n}) {\n const { storybookContext } = props\n\n const currentStack: SynapseStack = (storybookContext.globals.stack ||\n storybookContext.parameters.stack) as SynapseStack\n\n useEffect(() => {\n overrideEndpoint(currentStack)\n }, [currentStack])\n\n // Subscribe to the framework-agnostic SynapseSessionManager for token/auth state\n // These methods are bound in the SynapseSessionManager constructor, so they are safe to pass directly.\n const sessionState: SessionState = useSyncExternalStore(\n // oxlint-disable-next-line @typescript-eslint/unbound-method\n sessionManager.subscribe,\n // oxlint-disable-next-line @typescript-eslint/unbound-method\n sessionManager.getSnapshot,\n )\n\n // Track whether we've started the manager so we only start once (module-level instance persists across renders)\n const managerStartedRef = useRef(false)\n\n useEffect(() => {\n if (!managerStartedRef.current) {\n sessionManager.start()\n managerStartedRef.current = true\n }\n return () => {\n sessionManager.dispose()\n managerStartedRef.current = false\n }\n }, [])\n\n // When the stack changes, restart the session manager to pick up the new endpoint\n useEffect(() => {\n if (managerStartedRef.current) {\n sessionManager.dispose()\n sessionManager.start()\n }\n }, [currentStack])\n\n const shouldPromptForLogin =\n storybookContext.parameters.requireLogin &&\n currentStack !== 'mock' &&\n sessionState.hasInitializedSession &&\n !sessionState.isAuthenticated\n\n const { isLoading: isLoadingSSO } = useDetectSSOCode({\n onSignInComplete: () => {\n void sessionManager.refreshSession()\n },\n onError: (err: unknown) => {\n console.error('SSO error in Storybook:', err)\n },\n isInitializingSession: !sessionState.hasInitializedSession,\n isAuthenticated: sessionState.isAuthenticated,\n })\n\n useEffect(() => {\n async function resetCache() {\n await storybookQueryClient.cancelQueries()\n await storybookQueryClient.resetQueries()\n }\n\n void resetCache()\n }, [sessionState.token, currentStack])\n\n const effectiveToken = useMemo(() => {\n if (currentStack === 'mock') {\n return storybookContext.args.isAuthenticated ? 'fake token' : undefined\n }\n return sessionState.token\n }, [sessionState.token, currentStack, storybookContext.args.isAuthenticated])\n\n const effectiveIsAuthenticated = useMemo(() => {\n if (currentStack === 'mock') {\n return !!storybookContext.args.isAuthenticated\n }\n return sessionState.isAuthenticated\n }, [\n sessionState.isAuthenticated,\n currentStack,\n storybookContext.args.isAuthenticated,\n ])\n\n const refreshSession = useCallback(async () => {\n await sessionManager.refreshSession()\n }, [])\n\n const clearSession = useCallback(async () => {\n await sessionManager.clearSession()\n }, [])\n\n const applicationSessionContext = useMemo(\n () => ({\n token: effectiveToken,\n realmId: sessionState.realmId,\n userId: sessionState.userId,\n isAuthenticated: effectiveIsAuthenticated,\n hasInitializedSession: sessionState.hasInitializedSession,\n refreshSession,\n clearSession,\n isLoadingSSO,\n twoFactorAuthSSOErrorResponse: undefined,\n termsOfServiceStatus: undefined,\n twoFactorStatus: undefined,\n }),\n [\n effectiveToken,\n effectiveIsAuthenticated,\n sessionState.realmId,\n sessionState.userId,\n sessionState.hasInitializedSession,\n refreshSession,\n clearSession,\n isLoadingSSO,\n ],\n )\n\n const synapseContext: Partial<SynapseContextType> = useMemo(\n () => ({\n accessToken: effectiveToken,\n isAuthenticated: effectiveIsAuthenticated,\n isInExperimentalMode: SynapseClient.isInSynapseExperimentalMode(),\n utcTime: SynapseClient.getUseUtcTimeFromCookie(),\n withErrorBoundary: true,\n downloadCartPageUrl: '/?path=/story/download-downloadcartpage--demo',\n }),\n [effectiveToken, effectiveIsAuthenticated],\n )\n\n const wrappedStory = (\n <Suspense fallback={'global suspense loading...'}>\n <QueryClientProvider client={storybookQueryClient}>\n <ApplicationSessionContextProvider context={applicationSessionContext}>\n <SynapseContextProvider\n key={currentStack}\n synapseContext={synapseContext}\n >\n {storybookContext.globals.showReactQueryDevtools && (\n <ReactQueryDevtools />\n )}\n <SynapseToastContainer />\n <main>\n {shouldPromptForLogin ? (\n <StandaloneLoginForm\n sessionCallback={() => {\n void sessionManager.refreshSession()\n }}\n />\n ) : (\n props.children\n )}\n </main>\n </SynapseContextProvider>\n </ApplicationSessionContextProvider>\n </QueryClientProvider>\n </Suspense>\n )\n\n if (!storybookContext.parameters.withRouter) {\n return wrappedStory\n }\n\n const router = createMemoryRouter([\n {\n path: '/',\n element: wrappedStory,\n },\n ])\n\n return <RouterProvider router={router} />\n}\n\nexport default StorybookComponentWrapper\n"],"mappings":"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"}
|