Skip to content

Commit f87b2da

Browse files
authored
Merge pull request #19215 from GordonSmith/HPCC-32834-WU_FULLSCREEN
HPCC-32834 Persist fullscreen mode in address bar
2 parents 1b29505 + 75aebaf commit f87b2da

File tree

4 files changed

+109
-82
lines changed

4 files changed

+109
-82
lines changed

esp/src/src-react/components/Metrics.tsx

+28-13
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { HolyGrail } from "../layouts/HolyGrail";
1515
import { AutosizeComponent, AutosizeHpccJSComponent } from "../layouts/HpccJSAdapter";
1616
import { DockPanel, DockPanelItem, ResetableDockPanel } from "../layouts/DockPanel";
1717
import { LayoutStatus, MetricGraph, MetricGraphWidget, isGraphvizWorkerResponse, layoutCache } from "../util/metricGraph";
18-
import { pushUrl } from "../util/history";
18+
import { pushUrl as _pushUrl } from "../util/history";
1919
import { debounce } from "../util/throttle";
2020
import { ErrorBoundary } from "../util/errorBoundary";
2121
import { ShortVerticalDivider } from "./Common";
@@ -45,14 +45,16 @@ interface MetricsProps {
4545
queryId?: string;
4646
parentUrl?: string;
4747
selection?: string;
48+
fullscreen?: boolean;
4849
}
4950

5051
export const Metrics: React.FunctionComponent<MetricsProps> = ({
5152
wuid,
5253
querySet = "",
5354
queryId = "",
5455
parentUrl = `/workunits/${wuid}/metrics`,
55-
selection
56+
selection,
57+
fullscreen = false
5658
}) => {
5759
const [_uiState, _setUIState] = React.useState({ ...defaultUIState });
5860
const [selectedMetricsSource, setSelectedMetricsSource] = React.useState<SelectedMetricsSource>("");
@@ -63,7 +65,6 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
6365
const [showMetricOptions, setShowMetricOptions] = React.useState(false);
6466
const [dockpanel, setDockpanel] = React.useState<ResetableDockPanel>();
6567
const [trackSelection, setTrackSelection] = React.useState<boolean>(true);
66-
const [fullscreen, setFullscreen] = React.useState<boolean>(false);
6768
const [hotspots, setHotspots] = React.useState<string>("");
6869
const [lineage, setLineage] = React.useState<IScope[]>([]);
6970
const [selectedLineage, setSelectedLineage] = React.useState<IScope>();
@@ -96,10 +97,24 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
9697
}).catch(err => logger.error(err));
9798
}, [wuid]);
9899

100+
const pushUrl = React.useCallback((selection?: string, fullscreen?: boolean) => {
101+
const selectionStr = selection?.length ? `/${selection}` : "";
102+
const fullscreenStr = fullscreen ? "?fullscreen" : "";
103+
_pushUrl(`${parentUrl}${selectionStr}${fullscreenStr}`);
104+
}, [parentUrl]);
105+
106+
const pushSelectionUrl = React.useCallback((selection: string) => {
107+
pushUrl(selection, fullscreen);
108+
}, [fullscreen, pushUrl]);
109+
110+
const pushFullscreenUrl = React.useCallback((fullscreen: boolean) => {
111+
pushUrl(selection, fullscreen);
112+
}, [pushUrl, selection]);
113+
99114
const onHotspot = React.useCallback(() => {
100115
setSelectedMetricsSource("hotspot");
101-
pushUrl(`${parentUrl}/${selection}`);
102-
}, [parentUrl, selection]);
116+
pushSelectionUrl(selection);
117+
}, [pushSelectionUrl, selection]);
103118

104119
// Timeline ---
105120
const timeline = useConst(() => new WUTimelineNoFetch()
@@ -114,11 +129,11 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
114129
timeline.selection([]);
115130
setSelectedMetricsSource("scopesTable");
116131
setScopeFilter(`name:${row[7].__hpcc_id}`);
117-
pushUrl(`${parentUrl}/${row[7].id}`);
132+
pushSelectionUrl(row[7].id);
118133
}
119134
}, true)
120135
;
121-
}, [parentUrl, timeline]);
136+
}, [pushSelectionUrl, timeline]);
122137

123138
React.useEffect(() => {
124139
if (view.showTimeline) {
@@ -142,10 +157,10 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
142157
.on("selectionChanged", () => {
143158
const selection = metricGraphWidget.selection().filter(id => metricGraph.item(id)).map(id => metricGraph.item(id).id);
144159
setSelectedMetricsSource("metricGraphWidget");
145-
pushUrl(`${parentUrl}/${selection.join(",")}`);
160+
pushSelectionUrl(selection.join(","));
146161
}, true)
147162
;
148-
}, [metricGraph, metricGraphWidget, parentUrl]);
163+
}, [metricGraph, metricGraphWidget, pushSelectionUrl]);
149164

150165
React.useEffect(() => {
151166
metricGraph.load(metrics);
@@ -301,8 +316,8 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
301316

302317
const scopesSelectionChanged = React.useCallback((source: SelectedMetricsSource, selection: IScope[]) => {
303318
setSelectedMetricsSource(source);
304-
pushUrl(`${parentUrl}/${selection.map(row => row.__lparam?.id ?? row.id).join(",")}`);
305-
}, [parentUrl]);
319+
pushSelectionUrl(selection.map(row => row.__lparam?.id ?? row.id).join(","));
320+
}, [pushSelectionUrl]);
306321

307322
const scopesTable = useConst(() => new ScopesTable()
308323
.multiSelect(true)
@@ -520,9 +535,9 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
520535
{ key: "divider_2", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
521536
{
522537
key: "fullscreen", title: nlsHPCC.MaximizeRestore, iconProps: { iconName: fullscreen ? "ChromeRestore" : "FullScreen" },
523-
onClick: () => setFullscreen(!fullscreen)
538+
onClick: () => pushFullscreenUrl(!fullscreen)
524539
}
525-
], [dot, formatColumns, fullscreen, metrics, wuid]);
540+
], [dot, formatColumns, fullscreen, metrics, pushFullscreenUrl, wuid]);
526541

527542
const setShowMetricOptionsHook = React.useCallback((show: boolean) => {
528543
setShowMetricOptions(show);

esp/src/src-react/components/WorkunitDetails.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ interface WorkunitDetailsProps {
4040
parentUrl?: string;
4141
tab?: string;
4242
state?: { outputs?: string, metrics?: string, resources?: string, helpers?: string, eclsummary?: string };
43-
queryParams?: { outputs?: StringStringMap, inputs?: StringStringMap, resources?: StringStringMap, helpers?: StringStringMap, logs?: StringStringMap };
43+
queryParams?: { summary?: StringStringMap, outputs?: StringStringMap, inputs?: StringStringMap, metrics?: StringStringMap, resources?: StringStringMap, helpers?: StringStringMap, logs?: StringStringMap };
4444
}
4545

4646
export const WorkunitDetails: React.FunctionComponent<WorkunitDetailsProps> = ({
@@ -177,7 +177,7 @@ export const WorkunitDetails: React.FunctionComponent<WorkunitDetailsProps> = ({
177177
<div style={{ height: "100%" }}>
178178
<OverflowTabList tabs={tabs} selected={tab} onTabSelect={onTabSelect} size="medium" />
179179
<DelayLoadedPanel visible={tab === "summary"} size={size}>
180-
<WorkunitSummary wuid={wuid} />
180+
<WorkunitSummary wuid={wuid} fullscreen={queryParams.summary?.fullscreen !== undefined} />
181181
</DelayLoadedPanel>
182182
<DelayLoadedPanel visible={tab === "variables"} size={size}>
183183
<Variables wuid={wuid} />
@@ -202,7 +202,7 @@ export const WorkunitDetails: React.FunctionComponent<WorkunitDetailsProps> = ({
202202
<Shimmer />
203203
</>
204204
}>
205-
<Metrics wuid={wuid} parentUrl={`${parentUrl}/${wuid}/metrics`} selection={state?.metrics} />
205+
<Metrics wuid={wuid} parentUrl={`${parentUrl}/${wuid}/metrics`} selection={state?.metrics} fullscreen={queryParams.metrics?.fullscreen !== undefined} />
206206
</React.Suspense>
207207
</DelayLoadedPanel>
208208
<DelayLoadedPanel visible={tab === "workflows"} size={size}>

esp/src/src-react/components/WorkunitSummary.tsx

+77-65
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useConfirm } from "../hooks/confirm";
99
import { useWorkunit, useWorkunitExceptions } from "../hooks/workunit";
1010
import { ReflexContainer, ReflexElement, ReflexSplitter } from "../layouts/react-reflex";
1111
import { pushUrl, replaceUrl } from "../util/history";
12+
import { HolyGrail } from "../layouts/HolyGrail";
1213
import { ShortVerticalDivider } from "./Common";
1314
import { TableGroup } from "./forms/Groups";
1415
import { PublishQueryForm } from "./forms/PublishQuery";
@@ -26,10 +27,12 @@ interface MessageBarContent {
2627

2728
interface WorkunitSummaryProps {
2829
wuid: string;
30+
fullscreen?: boolean;
2931
}
3032

3133
export const WorkunitSummary: React.FunctionComponent<WorkunitSummaryProps> = ({
32-
wuid
34+
wuid,
35+
fullscreen = false
3336
}) => {
3437

3538
const [workunit, , , , refresh] = useWorkunit(wuid, true);
@@ -171,6 +174,13 @@ export const WorkunitSummary: React.FunctionComponent<WorkunitSummaryProps> = ({
171174
},
172175
], [_protected, canDelete, canDeschedule, canReschedule, canSave, description, jobname, refresh, refreshSavings, setShowDeleteConfirm, showMessageBar, workunit, wuid]);
173176

177+
const rightButtons = React.useMemo((): ICommandBarItemProps[] => [
178+
{
179+
key: "fullscreen", title: nlsHPCC.MaximizeRestore, iconProps: { iconName: fullscreen ? "ChromeRestore" : "FullScreen" },
180+
onClick: () => pushUrl(`/workunits/${wuid}${fullscreen ? "" : "?fullscreen"}`)
181+
}
182+
], [fullscreen, wuid]);
183+
174184
const serviceNames = React.useMemo(() => {
175185
return workunit?.ServiceNames?.Item?.join("\n") || "";
176186
}, [workunit?.ServiceNames?.Item]);
@@ -190,68 +200,70 @@ export const WorkunitSummary: React.FunctionComponent<WorkunitSummaryProps> = ({
190200
}, 0) || 0;
191201
}, [exceptions]);
192202

193-
return <>
194-
<ReflexContainer orientation="horizontal">
195-
<ReflexElement>
196-
<div className="pane-content">
197-
<ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
198-
<Sticky stickyPosition={StickyPositionType.Header}>
199-
<CommandBar items={buttons} />
200-
{messageBarContent &&
201-
<MessageBar messageBarType={messageBarContent.type} dismissButtonAriaLabel={nlsHPCC.Close} onDismiss={dismissMessageBar} >
202-
{messageBarContent.message}
203-
</MessageBar>
204-
}
205-
</Sticky>
206-
<Sticky stickyPosition={StickyPositionType.Header}>
207-
<WorkunitPersona wuid={wuid} />
208-
<div style={{ width: "512px", height: "64px", float: "right" }}>
209-
<WUStatus wuid={wuid}></WUStatus>
210-
</div>
211-
</Sticky>
212-
<TableGroup fields={{
213-
"wuid": { label: nlsHPCC.WUID, type: "string", value: wuid, readonly: true },
214-
"action": { label: nlsHPCC.Action, type: "string", value: workunit?.ActionEx, readonly: true },
215-
"state": { label: nlsHPCC.State, type: "string", value: workunit?.State + (workunit?.StateEx ? ` (${workunit.StateEx})` : ""), readonly: true },
216-
"owner": { label: nlsHPCC.Owner, type: "string", value: workunit?.Owner, readonly: true },
217-
"jobname": { label: nlsHPCC.JobName, type: "string", value: jobname },
218-
"description": { label: nlsHPCC.Description, type: "string", value: description },
219-
"potentialSavings": { label: nlsHPCC.PotentialSavings, type: "string", value: `${formatCost(potentialSavings)} (${totalCosts > 0 ? Math.round((potentialSavings / totalCosts) * 10000) / 100 : 0}%)`, readonly: true },
220-
"compileCost": { label: nlsHPCC.CompileCost, type: "string", value: `${formatCost(workunit?.CompileCost)}`, readonly: true },
221-
"executeCost": { label: nlsHPCC.ExecuteCost, type: "string", value: `${formatCost(workunit?.ExecuteCost)}`, readonly: true },
222-
"fileAccessCost": { label: nlsHPCC.FileAccessCost, type: "string", value: `${formatCost(workunit?.FileAccessCost)}`, readonly: true },
223-
"protected": { label: nlsHPCC.Protected, type: "checkbox", value: _protected },
224-
"cluster": { label: nlsHPCC.Cluster, type: "string", value: workunit?.Cluster, readonly: true },
225-
"totalClusterTime": { label: nlsHPCC.TotalClusterTime, type: "string", value: workunit?.TotalClusterTime ? workunit?.TotalClusterTime : "0.00", readonly: true },
226-
"abortedBy": { label: nlsHPCC.AbortedBy, type: "string", value: workunit?.AbortBy, readonly: true },
227-
"abortedTime": { label: nlsHPCC.AbortedTime, type: "string", value: workunit?.AbortTime, readonly: true },
228-
"ServiceNamesCustom": { label: nlsHPCC.Services, type: "string", value: serviceNames, readonly: true, multiline: true },
229-
}} onChange={(id, value) => {
230-
switch (id) {
231-
case "jobname":
232-
setJobname(value);
233-
break;
234-
case "description":
235-
setDescription(value);
236-
break;
237-
case "protected":
238-
setProtected(value);
239-
break;
240-
default:
241-
logger.debug(`${id}: ${value}`);
242-
}
243-
}} />
244-
</ScrollablePane>
245-
</div>
246-
</ReflexElement>
247-
<ReflexSplitter />
248-
<ReflexElement>
249-
<InfoGrid wuid={wuid}></InfoGrid>
250-
</ReflexElement>
251-
</ReflexContainer>
252-
<PublishQueryForm wuid={wuid} showForm={showPublishForm} setShowForm={setShowPublishForm} />
253-
<ZAPDialog wuid={wuid} showForm={showZapForm} setShowForm={setShowZapForm} />
254-
<SlaveLogs wuid={wuid} showForm={showThorSlaveLogs} setShowForm={setShowThorSlaveLogs} />
255-
<DeleteConfirm />
256-
</>;
203+
return <HolyGrail fullscreen={fullscreen}
204+
main={<>
205+
<ReflexContainer orientation="horizontal">
206+
<ReflexElement>
207+
<div className="pane-content">
208+
<ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
209+
<Sticky stickyPosition={StickyPositionType.Header}>
210+
<CommandBar items={buttons} farItems={rightButtons} />
211+
{messageBarContent &&
212+
<MessageBar messageBarType={messageBarContent.type} dismissButtonAriaLabel={nlsHPCC.Close} onDismiss={dismissMessageBar} >
213+
{messageBarContent.message}
214+
</MessageBar>
215+
}
216+
</Sticky>
217+
<Sticky stickyPosition={StickyPositionType.Header}>
218+
<WorkunitPersona wuid={wuid} />
219+
<div style={{ width: "512px", height: "64px", float: "right" }}>
220+
<WUStatus wuid={wuid}></WUStatus>
221+
</div>
222+
</Sticky>
223+
<TableGroup fields={{
224+
"wuid": { label: nlsHPCC.WUID, type: "string", value: wuid, readonly: true },
225+
"action": { label: nlsHPCC.Action, type: "string", value: workunit?.ActionEx, readonly: true },
226+
"state": { label: nlsHPCC.State, type: "string", value: workunit?.State + (workunit?.StateEx ? ` (${workunit.StateEx})` : ""), readonly: true },
227+
"owner": { label: nlsHPCC.Owner, type: "string", value: workunit?.Owner, readonly: true },
228+
"jobname": { label: nlsHPCC.JobName, type: "string", value: jobname },
229+
"description": { label: nlsHPCC.Description, type: "string", value: description },
230+
"potentialSavings": { label: nlsHPCC.PotentialSavings, type: "string", value: `${formatCost(potentialSavings)} (${totalCosts > 0 ? Math.round((potentialSavings / totalCosts) * 10000) / 100 : 0}%)`, readonly: true },
231+
"compileCost": { label: nlsHPCC.CompileCost, type: "string", value: `${formatCost(workunit?.CompileCost)}`, readonly: true },
232+
"executeCost": { label: nlsHPCC.ExecuteCost, type: "string", value: `${formatCost(workunit?.ExecuteCost)}`, readonly: true },
233+
"fileAccessCost": { label: nlsHPCC.FileAccessCost, type: "string", value: `${formatCost(workunit?.FileAccessCost)}`, readonly: true },
234+
"protected": { label: nlsHPCC.Protected, type: "checkbox", value: _protected },
235+
"cluster": { label: nlsHPCC.Cluster, type: "string", value: workunit?.Cluster, readonly: true },
236+
"totalClusterTime": { label: nlsHPCC.TotalClusterTime, type: "string", value: workunit?.TotalClusterTime ? workunit?.TotalClusterTime : "0.00", readonly: true },
237+
"abortedBy": { label: nlsHPCC.AbortedBy, type: "string", value: workunit?.AbortBy, readonly: true },
238+
"abortedTime": { label: nlsHPCC.AbortedTime, type: "string", value: workunit?.AbortTime, readonly: true },
239+
"ServiceNamesCustom": { label: nlsHPCC.Services, type: "string", value: serviceNames, readonly: true, multiline: true },
240+
}} onChange={(id, value) => {
241+
switch (id) {
242+
case "jobname":
243+
setJobname(value);
244+
break;
245+
case "description":
246+
setDescription(value);
247+
break;
248+
case "protected":
249+
setProtected(value);
250+
break;
251+
default:
252+
logger.debug(`${id}: ${value}`);
253+
}
254+
}} />
255+
</ScrollablePane>
256+
</div>
257+
</ReflexElement>
258+
<ReflexSplitter />
259+
<ReflexElement>
260+
<InfoGrid wuid={wuid}></InfoGrid>
261+
</ReflexElement>
262+
</ReflexContainer>
263+
<PublishQueryForm wuid={wuid} showForm={showPublishForm} setShowForm={setShowPublishForm} />
264+
<ZAPDialog wuid={wuid} showForm={showZapForm} setShowForm={setShowZapForm} />
265+
<SlaveLogs wuid={wuid} showForm={showThorSlaveLogs} setShowForm={setShowThorSlaveLogs} />
266+
<DeleteConfirm />
267+
</>}
268+
/>;
257269
};

esp/src/src-react/routes.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const workunitsChildren: Route[] = [
2525
},
2626
{
2727
path: "/:Wuid", action: (ctx, params) => import("./components/WorkunitDetails").then(_ => {
28-
return <_.WorkunitDetails wuid={params.Wuid as string} parentUrl={params.parentUrl as string} />;
28+
return <_.WorkunitDetails wuid={params.Wuid as string} parentUrl={params.parentUrl as string} queryParams={{ summary: parseSearch(ctx.search) as any }} />;
2929
})
3030
},
3131
{

0 commit comments

Comments
 (0)