Skip to content
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
merged 3 commits into from
Jul 21, 2023

Conversation

kyle-sammons
Copy link
Collaborator

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 [ ])

The following point can be removed after setting up CI (such as Travis) with coverage reports (such as Codecov)

  • I've written tests to cover the new code and functionality included in this PR.

The following point can be removed after setting up a CLA reporting tool such as cla-assistant.io

@github-actions
Copy link

Here are some suggestions to improve the code:

In src/datasource/components/FieldValueFrequency.tsx:

  1. Use destructuring assignment to extract the field and value from the valueFreq object in InnerContent function.
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>&quot;&quot; (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:

  1. Use destructuring assignment to extract the fields and topTenMostPopularFields from the model.useState() call in KalDBFieldsRenderer function.
const KalDBFieldsRenderer = ({ model }: SceneComponentProps<FieldStats>) => {
  // TODO: Loading state
  // const { timeseriesLoading, logsLoading } = model.useState();
  const { fields, topTenMostPopularFields, visible, loading } = model.useState();
  1. Use object destructuring to extract the field and value from the field parameter in the onPlusClick and onMinusClick callbacks in KalDBFieldsList function.
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>
  );
};
  1. Use object destructuring to extract the total and failed from the model.useState() call in NodeStatsRenderer function.
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}
    </>
  );
};
  1. Use object destructuring to extract the results from the model.useState() call in ResultsStatsRenderer function.
const ResultsStatsRenderer = ({ model }: SceneComponentProps<ResultStats>) => {
  const { results } = model.useState();

  return <>{results > -1 ? <h5>{results.toLocaleString('en-US')} hits</h5> : <h5></h5>}</>;
};
  1. Use object destructuring to extract the timeseriesLoading and logsLoading from the model.useState() call in KaldbQueryRenderer function.
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>
      )}
    </>
  );
};
  1. Use object destructuring to extract the total and failed from the state parameter in the NodeStats constructor.
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,
    });
  };
}
  1. Use object destructuring to extract the fields, topTenMostPopularFields, visible, and loading from the state parameter in the FieldStats constructor.
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,
    });
  };
}
  1. Use object destructuring to extract the query, timeseriesLoading, and logsLoading from the state parameter in the KaldbQuery constructor.
class KaldbQuery extends SceneObjectBase<KaldbQueryState> {
  static Component = KaldbQueryRenderer;

  constructor(state?: Partial<KaldbQueryState>) {
    super({
      query: '',
      timeseriesLoading: false,
      logsLoading: false,
      ...state,
    });
  }

  doQuery = () => {
    queryStringVariable.setValue(this

src/pages/explore.tsx Show resolved Hide resolved
src/pages/explore.tsx Show resolved Hide resolved
@kyle-sammons kyle-sammons merged commit 224066b into master Jul 21, 2023
1 check passed
@kyle-sammons kyle-sammons deleted the ksammons-MON-5382-Touch-Ups branch July 21, 2023 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants