mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
data-trails: ability to (de)serialize parents and current index (#78782)
fix: ability to (de)serialize parents and current index - refactored so only history stores parent relations and current index - rounded indirect parent links
This commit is contained in:
parent
318f51eaee
commit
ac1b9e44a2
@ -36,18 +36,14 @@ describe('DataTrail', () => {
|
|||||||
expect(trail.state.topScene).toBeInstanceOf(MetricSelectScene);
|
expect(trail.state.topScene).toBeInstanceOf(MetricSelectScene);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should set stepIndex to 0', () => {
|
it('Should set history current step to 0', () => {
|
||||||
expect(trail.state.stepIndex).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should set parentIndex to -1', () => {
|
|
||||||
expect(trail.state.parentIndex).toBe(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should set history currentStep to 0', () => {
|
|
||||||
expect(trail.state.history.state.currentStep).toBe(0);
|
expect(trail.state.history.state.currentStep).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should set history step 0 parentIndex to -1', () => {
|
||||||
|
expect(trail.state.history.state.steps[0].parentIndex).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
describe('And metric is selected', () => {
|
describe('And metric is selected', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
trail.publishEvent(new MetricSelectedEvent('metric_bucket'));
|
trail.publishEvent(new MetricSelectedEvent('metric_bucket'));
|
||||||
@ -66,16 +62,16 @@ describe('DataTrail', () => {
|
|||||||
expect(trail.state.history.state.steps[1].type).toBe('metric');
|
expect(trail.state.history.state.steps[1].type).toBe('metric');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should set stepIndex to 1', () => {
|
it('Should set history current step to 1', () => {
|
||||||
expect(trail.state.stepIndex).toBe(1);
|
expect(trail.state.history.state.currentStep).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should set history currentStep to 1', () => {
|
it('Should set history currentStep to 1', () => {
|
||||||
expect(trail.state.history.state.currentStep).toBe(1);
|
expect(trail.state.history.state.currentStep).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should set parentIndex to 0', () => {
|
it('Should set history step 1 parentIndex to 0', () => {
|
||||||
expect(trail.state.parentIndex).toBe(0);
|
expect(trail.state.history.state.steps[1].parentIndex).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,7 +79,7 @@ describe('DataTrail', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
trail.publishEvent(new MetricSelectedEvent('first_metric'));
|
trail.publishEvent(new MetricSelectedEvent('first_metric'));
|
||||||
trail.publishEvent(new MetricSelectedEvent('second_metric'));
|
trail.publishEvent(new MetricSelectedEvent('second_metric'));
|
||||||
trail.goBackToStep(trail.state.history.state.steps[1]);
|
trail.state.history.goBackToStep(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should restore state and url', () => {
|
it('Should restore state and url', () => {
|
||||||
@ -91,10 +87,6 @@ describe('DataTrail', () => {
|
|||||||
expect(locationService.getSearchObject().metric).toBe('first_metric');
|
expect(locationService.getSearchObject().metric).toBe('first_metric');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should set stepIndex to 1', () => {
|
|
||||||
expect(trail.state.stepIndex).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should set history currentStep to 1', () => {
|
it('Should set history currentStep to 1', () => {
|
||||||
expect(trail.state.history.state.currentStep).toBe(1);
|
expect(trail.state.history.state.currentStep).toBe(1);
|
||||||
});
|
});
|
||||||
@ -112,16 +104,12 @@ describe('DataTrail', () => {
|
|||||||
expect(trail.state.history.state.steps.length).toBe(4);
|
expect(trail.state.history.state.steps.length).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should set stepIndex to 3', () => {
|
it('Should set history current step to 3', () => {
|
||||||
expect(trail.state.stepIndex).toBe(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should set history currentStep to 1', () => {
|
|
||||||
expect(trail.state.history.state.currentStep).toBe(3);
|
expect(trail.state.history.state.currentStep).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should set parentIndex to 1', () => {
|
it('Should set history step 3 parent index to 1', () => {
|
||||||
expect(trail.state.parentIndex).toBe(1);
|
expect(trail.state.history.state.steps[3].parentIndex).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -44,10 +44,6 @@ export interface DataTrailState extends SceneObjectState {
|
|||||||
|
|
||||||
// Synced with url
|
// Synced with url
|
||||||
metric?: string;
|
metric?: string;
|
||||||
|
|
||||||
// Indicates which step in the data trail this is
|
|
||||||
stepIndex: number;
|
|
||||||
parentIndex: number; // If there is no parent, this will be -1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataTrail extends SceneObjectBase<DataTrailState> {
|
export class DataTrail extends SceneObjectBase<DataTrailState> {
|
||||||
@ -65,8 +61,6 @@ export class DataTrail extends SceneObjectBase<DataTrailState> {
|
|||||||
],
|
],
|
||||||
history: state.history ?? new DataTrailHistory({}),
|
history: state.history ?? new DataTrailHistory({}),
|
||||||
settings: state.settings ?? new DataTrailSettings({}),
|
settings: state.settings ?? new DataTrailSettings({}),
|
||||||
stepIndex: state.stepIndex ?? 0,
|
|
||||||
parentIndex: state.parentIndex ?? -1,
|
|
||||||
...state,
|
...state,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -81,6 +75,29 @@ export class DataTrail extends SceneObjectBase<DataTrailState> {
|
|||||||
// Some scene elements publish this
|
// Some scene elements publish this
|
||||||
this.subscribeToEvent(MetricSelectedEvent, this._handleMetricSelectedEvent.bind(this));
|
this.subscribeToEvent(MetricSelectedEvent, this._handleMetricSelectedEvent.bind(this));
|
||||||
|
|
||||||
|
// Pay attention to changes in history (i.e., changing the step)
|
||||||
|
this.state.history.subscribeToState((newState, oldState) => {
|
||||||
|
const oldNumberOfSteps = oldState.steps.length;
|
||||||
|
const newNumberOfSteps = newState.steps.length;
|
||||||
|
|
||||||
|
const newStepWasAppended = newNumberOfSteps > oldNumberOfSteps;
|
||||||
|
|
||||||
|
if (newStepWasAppended) {
|
||||||
|
// Do nothing because the state is already up to date -- it created a new step!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldState.currentStep === newState.currentStep) {
|
||||||
|
// The same step was clicked on -- no need to change anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// History changed because a different node was selected
|
||||||
|
const step = newState.steps[newState.currentStep];
|
||||||
|
|
||||||
|
this.goBackToStep(step);
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (!this.state.embedded) {
|
if (!this.state.embedded) {
|
||||||
getUrlSyncManager().cleanUp(this);
|
getUrlSyncManager().cleanUp(this);
|
||||||
@ -89,7 +106,7 @@ export class DataTrail extends SceneObjectBase<DataTrailState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public goBackToStep(step: DataTrailHistoryStep) {
|
private goBackToStep(step: DataTrailHistoryStep) {
|
||||||
if (!this.state.embedded) {
|
if (!this.state.embedded) {
|
||||||
getUrlSyncManager().cleanUp(this);
|
getUrlSyncManager().cleanUp(this);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ export interface DataTrailHistoryStep {
|
|||||||
description: string;
|
description: string;
|
||||||
type: TrailStepType;
|
type: TrailStepType;
|
||||||
trailState: DataTrailState;
|
trailState: DataTrailState;
|
||||||
|
parentIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TrailStepType = 'filters' | 'time' | 'metric' | 'start';
|
export type TrailStepType = 'filters' | 'time' | 'metric' | 'start';
|
||||||
@ -37,6 +38,8 @@ export class DataTrailHistory extends SceneObjectBase<DataTrailsHistoryState> {
|
|||||||
this.addActivationHandler(this._onActivate.bind(this));
|
this.addActivationHandler(this._onActivate.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private stepTransitionInProgress = false;
|
||||||
|
|
||||||
public _onActivate() {
|
public _onActivate() {
|
||||||
const trail = getTrailFor(this);
|
const trail = getTrailFor(this);
|
||||||
|
|
||||||
@ -45,19 +48,6 @@ export class DataTrailHistory extends SceneObjectBase<DataTrailsHistoryState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trail.subscribeToState((newState, oldState) => {
|
trail.subscribeToState((newState, oldState) => {
|
||||||
// Check if new and old state are at the same step index
|
|
||||||
// Then we know this is a history transition
|
|
||||||
const isMovingThroughHistory = newState.stepIndex !== oldState.stepIndex;
|
|
||||||
|
|
||||||
if (isMovingThroughHistory) {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
currentStep: newState.stepIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newState.metric !== oldState.metric) {
|
if (newState.metric !== oldState.metric) {
|
||||||
if (this.state.steps.length === 1) {
|
if (this.state.steps.length === 1) {
|
||||||
// For the first step we want to update the starting state so that it contains data
|
// For the first step we want to update the starting state so that it contains data
|
||||||
@ -84,10 +74,13 @@ export class DataTrailHistory extends SceneObjectBase<DataTrailsHistoryState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public addTrailStep(trail: DataTrail, type: TrailStepType) {
|
public addTrailStep(trail: DataTrail, type: TrailStepType) {
|
||||||
// Update the trail's new current step state, and note its parent step.
|
if (this.stepTransitionInProgress) {
|
||||||
|
// Do not add trail steps when step transition is in progress
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const stepIndex = this.state.steps.length;
|
const stepIndex = this.state.steps.length;
|
||||||
const parentIndex = type === 'start' ? -1 : trail.state.stepIndex;
|
const parentIndex = type === 'start' ? -1 : this.state.currentStep;
|
||||||
trail.setState({ ...trail.state, stepIndex, parentIndex });
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
currentStep: stepIndex,
|
currentStep: stepIndex,
|
||||||
@ -97,11 +90,20 @@ export class DataTrailHistory extends SceneObjectBase<DataTrailsHistoryState> {
|
|||||||
description: 'Test',
|
description: 'Test',
|
||||||
type,
|
type,
|
||||||
trailState: sceneUtils.cloneSceneObjectState(trail.state, { history: this }),
|
trailState: sceneUtils.cloneSceneObjectState(trail.state, { history: this }),
|
||||||
|
parentIndex,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public goBackToStep(stepIndex: number) {
|
||||||
|
this.stepTransitionInProgress = true;
|
||||||
|
|
||||||
|
this.setState({ currentStep: stepIndex });
|
||||||
|
|
||||||
|
this.stepTransitionInProgress = false;
|
||||||
|
}
|
||||||
|
|
||||||
renderStepTooltip(step: DataTrailHistoryStep) {
|
renderStepTooltip(step: DataTrailHistoryStep) {
|
||||||
return (
|
return (
|
||||||
<Stack direction="column">
|
<Stack direction="column">
|
||||||
@ -114,20 +116,19 @@ export class DataTrailHistory extends SceneObjectBase<DataTrailsHistoryState> {
|
|||||||
public static Component = ({ model }: SceneComponentProps<DataTrailHistory>) => {
|
public static Component = ({ model }: SceneComponentProps<DataTrailHistory>) => {
|
||||||
const { steps, currentStep } = model.useState();
|
const { steps, currentStep } = model.useState();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const trail = getTrailFor(model);
|
|
||||||
|
|
||||||
const { ancestry, alternatePredecessorStyle } = useMemo(() => {
|
const { ancestry, alternatePredecessorStyle } = useMemo(() => {
|
||||||
const ancestry = new Set<number>();
|
const ancestry = new Set<number>();
|
||||||
let cursor = currentStep;
|
let cursor = currentStep;
|
||||||
while (cursor >= 0) {
|
while (cursor >= 0) {
|
||||||
ancestry.add(cursor);
|
ancestry.add(cursor);
|
||||||
cursor = steps[cursor].trailState.parentIndex;
|
cursor = steps[cursor].parentIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
const alternatePredecessorStyle = new Map<number, string>();
|
const alternatePredecessorStyle = new Map<number, string>();
|
||||||
|
|
||||||
ancestry.forEach((index) => {
|
ancestry.forEach((index) => {
|
||||||
const parent = steps[index].trailState.parentIndex;
|
const parent = steps[index].parentIndex;
|
||||||
if (parent + 1 !== index) {
|
if (parent + 1 !== index) {
|
||||||
alternatePredecessorStyle.set(index, createAlternatePredecessorStyle(index, parent));
|
alternatePredecessorStyle.set(index, createAlternatePredecessorStyle(index, parent));
|
||||||
}
|
}
|
||||||
@ -152,13 +153,13 @@ export class DataTrailHistory extends SceneObjectBase<DataTrailsHistoryState> {
|
|||||||
// To alter the look of steps with distant non-directly preceding parent
|
// To alter the look of steps with distant non-directly preceding parent
|
||||||
alternatePredecessorStyle.get(index) ?? '',
|
alternatePredecessorStyle.get(index) ?? '',
|
||||||
// To remove direct link for steps that don't have a direct parent
|
// To remove direct link for steps that don't have a direct parent
|
||||||
index !== step.trailState.parentIndex + 1 ? styles.stepOmitsDirectLeftLink : '',
|
index !== step.parentIndex + 1 ? styles.stepOmitsDirectLeftLink : '',
|
||||||
// To remove the direct parent link on the start node as well
|
// To remove the direct parent link on the start node as well
|
||||||
index === 0 ? styles.stepOmitsDirectLeftLink : '',
|
index === 0 ? styles.stepOmitsDirectLeftLink : '',
|
||||||
// To darken steps that aren't the current step's ancesters
|
// To darken steps that aren't the current step's ancesters
|
||||||
!ancestry.has(index) ? styles.stepIsNotAncestorOfCurrent : ''
|
!ancestry.has(index) ? styles.stepIsNotAncestorOfCurrent : ''
|
||||||
)}
|
)}
|
||||||
onClick={() => trail.goBackToStep(step)}
|
onClick={() => model.goBackToStep(index)}
|
||||||
></button>
|
></button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
@ -269,6 +270,8 @@ function createAlternatePredecessorStyle(index: number, parent: number) {
|
|||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderBottom: 'none',
|
borderBottom: 'none',
|
||||||
|
borderTopLeftRadius: 8,
|
||||||
|
borderTopRightRadius: 8,
|
||||||
top: -10,
|
top: -10,
|
||||||
left: 3 - distanceToParent,
|
left: 3 - distanceToParent,
|
||||||
background: 'none',
|
background: 'none',
|
||||||
|
@ -13,7 +13,9 @@ export interface SerializedTrail {
|
|||||||
urlValues: SceneObjectUrlValues;
|
urlValues: SceneObjectUrlValues;
|
||||||
type: TrailStepType;
|
type: TrailStepType;
|
||||||
description: string;
|
description: string;
|
||||||
|
parentIndex: number;
|
||||||
}>;
|
}>;
|
||||||
|
currentStep: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TrailStore {
|
export class TrailStore {
|
||||||
@ -55,9 +57,15 @@ export class TrailStore {
|
|||||||
|
|
||||||
t.history.map((step) => {
|
t.history.map((step) => {
|
||||||
this._loadFromUrl(trail, step.urlValues);
|
this._loadFromUrl(trail, step.urlValues);
|
||||||
|
const parentIndex = step.parentIndex ?? trail.state.history.state.steps.length - 1;
|
||||||
|
// Set the parent of the next trail step by setting the current step in history.
|
||||||
|
trail.state.history.setState({ currentStep: parentIndex });
|
||||||
trail.state.history.addTrailStep(trail, step.type);
|
trail.state.history.addTrailStep(trail, step.type);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const currentStep = t.currentStep ?? trail.state.history.state.steps.length - 1;
|
||||||
|
trail.state.history.setState({ currentStep });
|
||||||
|
|
||||||
return trail;
|
return trail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,10 +76,12 @@ export class TrailStore {
|
|||||||
urlValues: getUrlSyncManager().getUrlState(stepTrail),
|
urlValues: getUrlSyncManager().getUrlState(stepTrail),
|
||||||
type: step.type,
|
type: step.type,
|
||||||
description: step.description,
|
description: step.description,
|
||||||
|
parentIndex: step.parentIndex,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
history,
|
history,
|
||||||
|
currentStep: trail.state.history.state.currentStep,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user