<commit_before>import React, {PureComponent, ChangeEvent} from 'react'
import {connect} from 'react-redux'
import Dygraph from 'dygraphs'

import _ from 'lodash'
import classnames from 'classnames'
import uuid from 'uuid'

import * as actions from 'src/dashboards/actions'
import {SeriesLegendData} from 'src/types/dygraphs'
import DygraphLegendSort from 'src/shared/components/DygraphLegendSort'

import {makeLegendStyles, removeMeasurement} from 'src/shared/graphs/helpers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {NO_CELL} from 'src/shared/constants'

interface ExtendedDygraph extends Dygraph {
  graphDiv: HTMLElement
}

interface Props {
  dygraph: ExtendedDygraph
  cellID: string
  onHide: () => void
  onShow: (MouseEvent) => void
  activeCellID: string
  setActiveCell: (cellID: string) => void
}

interface LegendData {
  x: string | null
  series: SeriesLegendData[]
  xHTML: string
}

interface State {
  legend: LegendData
  sortType: string
  isAscending: boolean
  filterText: string
  isSnipped: boolean
  isFilterVisible: boolean
  legendStyles: object
  pageX: number | null
  cellID: string
}

@ErrorHandling
class DygraphLegend extends PureComponent<Props, State> {
  private legendRef: HTMLElement | null = null

  constructor(props) {
    super(props)

    this.props.dygraph.updateOptions({
      legendFormatter: this.legendFormatter,
      highlightCallback: this.highlightCallback,
      unhighlightCallback: this.unhighlightCallback,
    })

    this.state = {
      legend: {
        x: null,
        series: [],
        xHTML: '',
      },
      sortType: 'numeric',
      isAscending: false,
      filterText: '',
      isSnipped: false,
      isFilterVisible: false,
      legendStyles: {},
      pageX: null,
      cellID: null,
    }
  }

  public componentWillUnmount() {
    if (
      !this.props.dygraph.graphDiv ||
      !this.props.dygraph.visibility().find(bool => bool === true)
    ) {
      this.setState({filterText: ''})
    }
  }

  public render() {
    const {
      legend,
      filterText,
      isSnipped,
      isAscending,
      isFilterVisible,
    } = this.state

    return (
      <div
        className={`dygraph-legend ${this.hidden}`}
        ref={el => (this.legendRef = el)}
        onMouseLeave={this.handleHide}
        style={this.styles}
      >
        <div className="dygraph-legend--header">
          <div className="dygraph-legend--timestamp">{legend.xHTML}</div>
          <DygraphLegendSort
            isAscending={isAscending}
            isActive={this.isAphaSort}
            top="A"
            bottom="Z"
            onSort={this.handleSortLegend('alphabetic')}
          />
          <DygraphLegendSort
            isAscending={isAscending}
            isActive={this.isNumSort}
            top="0"
            bottom="9"
            onSort={this.handleSortLegend('numeric')}
          />
          <button
            className={classnames('btn btn-square btn-sm', {
              'btn-default': !isFilterVisible,
              'btn-primary': isFilterVisible,
            })}
            onClick={this.handleToggleFilter}
          >
            <span className="icon search" />
          </button>
          <button
            className={classnames('btn btn-sm', {
              'btn-default': !isSnipped,
              'btn-primary': isSnipped,
            })}
            onClick={this.handleSnipLabel}
          >
            Snip
          </button>
        </div>
        {isFilterVisible && (
          <input
            className="dygraph-legend--filter form-control input-sm"
            type="text"
            value={filterText}
            onChange={this.handleLegendInputChange}
            placeholder="Filter items..."
            autoFocus={true}
          />
        )}
        <div className="dygraph-legend--divider" />
        <div className="dygraph-legend--contents">
          {this.filtered.map(({label, color, yHTML, isHighlighted}) => {
            const seriesClass = isHighlighted
              ? 'dygraph-legend--row highlight'
              : 'dygraph-legend--row'
            return (
              <div key={uuid.v4()} className={seriesClass}>
                <span style={{color}}>
                  {isSnipped ? removeMeasurement(label) : label}
                </span>
                <figure>{yHTML || 'no value'}</figure>
              </div>
            )
          })}
        </div>
      </div>
    )
  }

  private handleHide = (): void => {
    this.props.onHide()
    this.props.setActiveCell(NO_CELL)
  }

  private handleToggleFilter = (): void => {
    this.setState({
      isFilterVisible: !this.state.isFilterVisible,
      filterText: '',
    })
  }

  private handleSnipLabel = (): void => {
    this.setState({isSnipped: !this.state.isSnipped})
  }

  private handleLegendInputChange = (
    e: ChangeEvent<HTMLInputElement>
  ): void => {
    const {dygraph} = this.props
    const {legend} = this.state
    const filterText = e.target.value

    legend.series.map((__, i) => {
      if (!legend.series[i]) {
        return dygraph.setVisibility(i, true)
      }

      dygraph.setVisibility(i, !!legend.series[i].label.match(filterText))
    })

    this.setState({filterText})<commit_msg>Remove legend snip and divider<commit_after>import React, {PureComponent, ChangeEvent} from 'react'
import {connect} from 'react-redux'
import Dygraph from 'dygraphs'

import _ from 'lodash'
import classnames from 'classnames'
import uuid from 'uuid'

import * as actions from 'src/dashboards/actions'
import {SeriesLegendData} from 'src/types/dygraphs'
import DygraphLegendSort from 'src/shared/components/DygraphLegendSort'

import {makeLegendStyles} from 'src/shared/graphs/helpers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {NO_CELL} from 'src/shared/constants'

interface ExtendedDygraph extends Dygraph {
  graphDiv: HTMLElement
}

interface Props {
  dygraph: ExtendedDygraph
  cellID: string
  onHide: () => void
  onShow: (MouseEvent) => void
  activeCellID: string
  setActiveCell: (cellID: string) => void
}

interface LegendData {
  x: string | null
  series: SeriesLegendData[]
  xHTML: string
}

interface State {
  legend: LegendData
  sortType: string
  isAscending: boolean
  filterText: string
  isFilterVisible: boolean
  legendStyles: object
  pageX: number | null
  cellID: string
}

@ErrorHandling
class DygraphLegend extends PureComponent<Props, State> {
  private legendRef: HTMLElement | null = null

  constructor(props) {
    super(props)

    this.props.dygraph.updateOptions({
      legendFormatter: this.legendFormatter,
      highlightCallback: this.highlightCallback,
      unhighlightCallback: this.unhighlightCallback,
    })

    this.state = {
      legend: {
        x: null,
        series: [],
        xHTML: '',
      },
      sortType: 'numeric',
      isAscending: false,
      filterText: '',
      isFilterVisible: false,
      legendStyles: {},
      pageX: null,
      cellID: null,
    }
  }

  public componentWillUnmount() {
    if (
      !this.props.dygraph.graphDiv ||
      !this.props.dygraph.visibility().find(bool => bool === true)
    ) {
      this.setState({filterText: ''})
    }
  }

  public render() {
    const {legend, filterText, isAscending, isFilterVisible} = this.state

    return (
      <div
        className={`dygraph-legend ${this.hidden}`}
        ref={el => (this.legendRef = el)}
        onMouseLeave={this.handleHide}
        style={this.styles}
      >
        <div className="dygraph-legend--header">
          <div className="dygraph-legend--timestamp">{legend.xHTML}</div>
          <DygraphLegendSort
            isAscending={isAscending}
            isActive={this.isAphaSort}
            top="A"
            bottom="Z"
            onSort={this.handleSortLegend('alphabetic')}
          />
          <DygraphLegendSort
            isAscending={isAscending}
            isActive={this.isNumSort}
            top="0"
            bottom="9"
            onSort={this.handleSortLegend('numeric')}
          />
          <button
            className={classnames('btn btn-square btn-xs', {
              'btn-default': !isFilterVisible,
              'btn-primary': isFilterVisible,
            })}
            onClick={this.handleToggleFilter}
          >
            <span className="icon search" />
          </button>
        </div>
        {isFilterVisible && (
          <input
            className="dygraph-legend--filter form-control input-xs"
            type="text"
            value={filterText}
            onChange={this.handleLegendInputChange}
            placeholder="Filter items..."
            autoFocus={true}
          />
        )}
        <div className="dygraph-legend--contents">
          {this.filtered.map(({label, color, yHTML, isHighlighted}) => {
            const seriesClass = isHighlighted
              ? 'dygraph-legend--row highlight'
              : 'dygraph-legend--row'
            return (
              <div key={uuid.v4()} className={seriesClass}>
                <span style={{color}}>{label}</span>
                <figure>{yHTML || 'no value'}</figure>
              </div>
            )
          })}
        </div>
      </div>
    )
  }

  private handleHide = (): void => {
    this.props.onHide()
    this.props.setActiveCell(NO_CELL)
  }

  private handleToggleFilter = (): void => {
    this.setState({
      isFilterVisible: !this.state.isFilterVisible,
      filterText: '',
    })
  }

  private handleLegendInputChange = (
    e: ChangeEvent<HTMLInputElement>
  ): void => {
    const {dygraph} = this.props
    const {legend} = this.state
    const filterText = e.target.value

    legend.series.map((__, i) => {
      if (!legend.series[i]) {
        return dygraph.setVisibility(i, true)
      }

      dygraph.setVisibility(i, !!legend.series[i].label.match(filterText))
    })

    this.setState({filterText})
{'fraction_non_alphanumeric': 0.1229607250755287, 'fraction_numerical': 0.0006042296072507553, 'mean_word_length': 2.2603414313854233, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6428253', 'n_tokens_mistral': 2994, 'n_tokens_neox': 2919, 'n_words': 560}