-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MON-5382] Added filter by click UI, folding, loading indicators, and various other touch ups #30
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Here are some suggestions to improve the code: In src/datasource/components/FieldValueFrequency.tsx:
const InnerContent = (
field: Field,
onPlusClick?: (field: Field, value: string) => void,
onMinusClick?: (field: Field, value: string) => void
) => {
return (
<div>
<span>
<b>
Top {field.mostCommonValues.length} {field.mostCommonValues.length > 1 ? 'values' : 'value'}
</b>
</span>
{field.mostCommonValues.map(({ value, frequency }) => {
return (
<VerticalGroup key={value}>
<HorizontalGroup spacing={'lg'} justify={'space-between'}>
<HorizontalGroup>
<span
style={{
fontFamily: 'monospace',
}}
>
{value === '""' ? <i>"" (empty) </i> : value}
</span>
</HorizontalGroup>
<HorizontalGroup spacing={'xs'} justify={'flex-end'} align={'flex-end'}>
<span>{(frequency * 100).toFixed(2)}%</span>
<Button
size={'sm'}
variant={'secondary'}
icon={'plus'}
onClick={() => (onPlusClick ? onPlusClick(field, value) : null)}
></Button>
<Button
size={'sm'}
variant={'secondary'}
icon={'minus'}
onClick={() => (onMinusClick ? onMinusClick(field, value) : null)}
></Button>
</HorizontalGroup>
</HorizontalGroup>
<div
style={{
width: `${(frequency * 100).toFixed(2)}%`,
height: '4px',
backgroundColor: 'green',
}}
></div>
</VerticalGroup>
);
})}
</div>
);
}; In src/pages/explore.tsx:
const KalDBFieldsRenderer = ({ model }: SceneComponentProps<FieldStats>) => {
// TODO: Loading state
// const { timeseriesLoading, logsLoading } = model.useState();
const { fields, topTenMostPopularFields, visible, loading } = model.useState();
const KalDBFieldsList = (fields: Field[], topTenMostPopularFields: Field[]) => {
const getIcon = (field: Field): string => {...};
const getTitle = (field: Field): string => {...};
return (
<div>
<div
style={{
backgroundColor: '#e6f1fa',
}}
>
<span
style={{
padding: '15px',
fontWeight: 'bold',
}}
>
Popular
</span>
<ul
className="fa-ul"
style={{
maxWidth: '250px',
}}
>
{topTenMostPopularFields.map((field) => (
<div key={field.name}>
<li
style={{
maxWidth: '200px',
cursor: 'pointer',
}}
>
<FieldValueFrequency
field={field}
onPlusClick={({ field, value }) => queryComponent.appendToQuery(`${field.name}: ${value}`)}
onMinusClick={({ field, value }) => queryComponent.appendToQuery(`NOT ${field.name}: ${value}`)}
>
<div>
<HorizontalGroup>
<i className={getIcon(field)} title={getTitle(field)} style={{ paddingTop: '12px' }}></i>
<span
style={{
paddingTop: '10px',
fontFamily: 'monospace',
}}
>
{field.name}
</span>
</HorizontalGroup>
</div>
</FieldValueFrequency>
</li>
</div>
))}
</ul>
</div>
<ul
className="fa-ul"
style={{
maxWidth: '250px',
}}
>
{fields.map((field) => (
<div key={field.name}>
<li
style={{
cursor: 'pointer',
}}
>
<FieldValueFrequency
field={field}
onPlusClick={({ field, value }) => queryComponent.appendToQuery(`${field.name}: ${value}`)}
onMinusClick={({ field, value }) => queryComponent.appendToQuery(`NOT ${field.name}: ${value}`)}
>
<div>
<HorizontalGroup>
<i className={getIcon(field)} title={getTitle(field)} style={{ paddingTop: '12px' }}></i>
<span
style={{
paddingTop: '10px',
fontFamily: 'monospace',
}}
>
{field.name}
</span>
</HorizontalGroup>
</div>
</FieldValueFrequency>
</li>
</div>
))}
</ul>
</div>
);
};
const NodeStatsRenderer = ({ model }: SceneComponentProps<NodeStats>) => {
const { total, failed } = model.useState();
return (
<>
{total > -1 && failed > -1 ? (
<IconButton name={'bug'} tooltip={`${total} nodes queried, ${failed} failed`}></IconButton>
) : null}
</>
);
};
const ResultsStatsRenderer = ({ model }: SceneComponentProps<ResultStats>) => {
const { results } = model.useState();
return <>{results > -1 ? <h5>{results.toLocaleString('en-US')} hits</h5> : <h5></h5>}</>;
};
const KaldbQueryRenderer = ({ model }: SceneComponentProps<KaldbQuery>) => {
const { timeseriesLoading, logsLoading } = model.useState();
return (
<>
<InlineField label="Query" grow={true}>
<Input
defaultValue={queryStringVariable.getValue().toString()}
// This is a bit of a hack to get the defaultValue to update after the user has made some changes and then
// we try to update the queryStringVariable for them again.
// Link to StackOverflow discussion: https://stackoverflow.com/questions/30146105/react-input-defaultvalue-doesnt-update-with-state
key={queryStringVariable.getValue().toString()}
placeholder="Lucene Query"
onKeyDown={(e) => (e.key === 'Enter' ? model.doQuery() : null)}
onChange={(e) => model.onTextChange(e.currentTarget.value)}
/>
</InlineField>
{timeseriesLoading || logsLoading ? (
<Button
icon="fa fa-spinner"
onClick={() => {
logsQueryRunner.cancelQuery();
histogramQueryRunner.cancelQuery();
}}
variant="destructive"
>
Cancel
</Button>
) : (
<Button icon="sync" onClick={model.doQuery}>
Run Query
</Button>
)}
</>
);
};
class NodeStats extends SceneObjectBase<NodeStatsState> {
static Component = NodeStatsRenderer;
constructor(state?: Partial<NodeStatsState>) {
super({
total: -1,
failed: -1,
...state,
});
}
setCount = (total: number, failed: number) => {
this.setState({
total: total,
failed: failed,
});
};
}
class FieldStats extends SceneObjectBase<FieldStatsState> {
static Component = KalDBFieldsRenderer;
constructor(state?: Partial<FieldStatsState>) {
super({
fields: [],
topTenMostPopularFields: [],
visible: true,
loading: true,
...state,
});
}
setTopTenMostPopularFields = (fields: Field[]) => {
this.setState({
topTenMostPopularFields: fields,
});
};
setLoading = (loading: boolean) => {
this.setState({
loading: loading,
});
};
setVisible = (visible: boolean) => {
this.setState({
visible: visible,
});
};
setFields = (fields: Field[]) => {
this.setState({
fields: fields,
});
};
}
class KaldbQuery extends SceneObjectBase<KaldbQueryState> {
static Component = KaldbQueryRenderer;
constructor(state?: Partial<KaldbQueryState>) {
super({
query: '',
timeseriesLoading: false,
logsLoading: false,
...state,
});
}
doQuery = () => {
queryStringVariable.setValue(this |
bryanlb
reviewed
Jul 21, 2023
bryanlb
approved these changes
Jul 21, 2023
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds the filter by click UI to the field values (i.e. click the "+" button to add a filter for that value to your query, click the "-" button to add a filter for not that value to your query), folding of the field list, a loading indicator to the field list, and a few other smaller touchups.
Screenshots
Various recordings of the functionality can be found below.
Filter by click
This video shows the filter by click behavior. It shows both positive and negative filters being added at the beginning of the query and when a query is already present.
Filter.By.Click.mov
Folding
This video shows the folding behavior. Ideally we'd have a smoother transition between the two states, but I was unable to get Grafana's transitions to play nicely.
Folding.mov
Loading indicator
This video shows the loading indicator at work
Loading.Indicator.mov
Requirements (place an
x
in each[ ]
)