<commit_before>import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { compact, isEmpty, invoke } from 'lodash';
import { markdown } from 'markdown';
import cx from 'classnames';
import Menu from 'antd/lib/menu';
import { currentUser } from '@/services/auth';
import recordEvent from '@/services/recordEvent';
import { formatDateTime } from '@/filters/datetime';
import HtmlContent from '@/components/HtmlContent';
import { Parameters } from '@/components/Parameters';
import { TimeAgo } from '@/components/TimeAgo';
import { Timer } from '@/components/Timer';
import { Moment } from '@/components/proptypes';
import QueryLink from '@/components/QueryLink';
import { FiltersType } from '@/components/Filters';
import ExpandedWidgetDialog from '@/components/dashboards/ExpandedWidgetDialog';
import EditParameterMappingsDialog from '@/components/dashboards/EditParameterMappingsDialog';
import { VisualizationRenderer } from '@/visualizations/VisualizationRenderer';
import Widget from './Widget';

function visualizationWidgetMenuOptions({ widget, canEditDashboard, onParametersEdit }) {
  const canViewQuery = currentUser.hasPermission('view_query');
  const canEditParameters = canEditDashboard && !isEmpty(invoke(widget, 'query.getParametersDefs'));
  const widgetQueryResult = widget.getQueryResult();
  const isQueryResultEmpty = !widgetQueryResult || !widgetQueryResult.isEmpty || widgetQueryResult.isEmpty();

  const downloadLink = fileType => widgetQueryResult.getLink(widget.getQuery().id, fileType);
  const downloadName = fileType => widgetQueryResult.getName(widget.getQuery().name, fileType);
  return compact([
    <Menu.Item key="download_csv" disabled={isQueryResultEmpty}>
      {!isQueryResultEmpty ? (
        <a href={downloadLink('csv')} download={downloadName('csv')} target="_self">
          Download as CSV File
        </a>
      ) : 'Download as CSV File'}
    </Menu.Item>,
    <Menu.Item key="download_excel" disabled={isQueryResultEmpty}>
      {!isQueryResultEmpty ? (
        <a href={downloadLink('xlsx')} download={downloadName('xlsx')} target="_self">
          Download as Excel File
        </a>
      ) : 'Download as Excel File'}
    </Menu.Item>,
    ((canViewQuery || canEditParameters) && <Menu.Divider key="divider" />),
    canViewQuery && (
      <Menu.Item key="view_query">
        <a href={widget.getQuery().getUrl(true, widget.visualization.id)}>View Query</a>
      </Menu.Item>
    ),
    (canEditParameters && (
      <Menu.Item
        key="edit_parameters"
        onClick={onParametersEdit}
      >
        Edit Parameters
      </Menu.Item>
    )),
  ]);
}

function RefreshIndicator({ refreshStartedAt }) {
  return (
    <div className="refresh-indicator">
      <div className="refresh-icon">
        <i className="zmdi zmdi-refresh zmdi-hc-spin" />
      </div>
      <Timer from={refreshStartedAt} />
    </div>
  );
}

RefreshIndicator.propTypes = { refreshStartedAt: Moment };
RefreshIndicator.defaultProps = { refreshStartedAt: null };

function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onParametersUpdate }) {
  const canViewQuery = currentUser.hasPermission('view_query');

  return (
    <>
      <RefreshIndicator refreshStartedAt={refreshStartedAt} />
      <div className="t-header widget clearfix">
        <div className="th-title">
          <p>
            <QueryLink query={widget.getQuery()} visualization={widget.visualization} readOnly={!canViewQuery} />
          </p>
          <HtmlContent className="text-muted query--description">
            {markdown.toHTML(widget.getQuery().description || '')}
          </HtmlContent>
        </div>
      </div>
      {!isEmpty(parameters) && (
        <div className="m-b-10">
          <Parameters parameters={parameters} onValuesChange={onParametersUpdate} />
        </div>
      )}
    </><commit_msg>Extend bolder markdown fix to widget description (#4229)<commit_after>import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { compact, isEmpty, invoke } from 'lodash';
import { markdown } from 'markdown';
import cx from 'classnames';
import Menu from 'antd/lib/menu';
import { currentUser } from '@/services/auth';
import recordEvent from '@/services/recordEvent';
import { formatDateTime } from '@/filters/datetime';
import HtmlContent from '@/components/HtmlContent';
import { Parameters } from '@/components/Parameters';
import { TimeAgo } from '@/components/TimeAgo';
import { Timer } from '@/components/Timer';
import { Moment } from '@/components/proptypes';
import QueryLink from '@/components/QueryLink';
import { FiltersType } from '@/components/Filters';
import ExpandedWidgetDialog from '@/components/dashboards/ExpandedWidgetDialog';
import EditParameterMappingsDialog from '@/components/dashboards/EditParameterMappingsDialog';
import { VisualizationRenderer } from '@/visualizations/VisualizationRenderer';
import Widget from './Widget';

function visualizationWidgetMenuOptions({ widget, canEditDashboard, onParametersEdit }) {
  const canViewQuery = currentUser.hasPermission('view_query');
  const canEditParameters = canEditDashboard && !isEmpty(invoke(widget, 'query.getParametersDefs'));
  const widgetQueryResult = widget.getQueryResult();
  const isQueryResultEmpty = !widgetQueryResult || !widgetQueryResult.isEmpty || widgetQueryResult.isEmpty();

  const downloadLink = fileType => widgetQueryResult.getLink(widget.getQuery().id, fileType);
  const downloadName = fileType => widgetQueryResult.getName(widget.getQuery().name, fileType);
  return compact([
    <Menu.Item key="download_csv" disabled={isQueryResultEmpty}>
      {!isQueryResultEmpty ? (
        <a href={downloadLink('csv')} download={downloadName('csv')} target="_self">
          Download as CSV File
        </a>
      ) : 'Download as CSV File'}
    </Menu.Item>,
    <Menu.Item key="download_excel" disabled={isQueryResultEmpty}>
      {!isQueryResultEmpty ? (
        <a href={downloadLink('xlsx')} download={downloadName('xlsx')} target="_self">
          Download as Excel File
        </a>
      ) : 'Download as Excel File'}
    </Menu.Item>,
    ((canViewQuery || canEditParameters) && <Menu.Divider key="divider" />),
    canViewQuery && (
      <Menu.Item key="view_query">
        <a href={widget.getQuery().getUrl(true, widget.visualization.id)}>View Query</a>
      </Menu.Item>
    ),
    (canEditParameters && (
      <Menu.Item
        key="edit_parameters"
        onClick={onParametersEdit}
      >
        Edit Parameters
      </Menu.Item>
    )),
  ]);
}

function RefreshIndicator({ refreshStartedAt }) {
  return (
    <div className="refresh-indicator">
      <div className="refresh-icon">
        <i className="zmdi zmdi-refresh zmdi-hc-spin" />
      </div>
      <Timer from={refreshStartedAt} />
    </div>
  );
}

RefreshIndicator.propTypes = { refreshStartedAt: Moment };
RefreshIndicator.defaultProps = { refreshStartedAt: null };

function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onParametersUpdate }) {
  const canViewQuery = currentUser.hasPermission('view_query');

  return (
    <>
      <RefreshIndicator refreshStartedAt={refreshStartedAt} />
      <div className="t-header widget clearfix">
        <div className="th-title">
          <p>
            <QueryLink query={widget.getQuery()} visualization={widget.visualization} readOnly={!canViewQuery} />
          </p>
          <HtmlContent className="text-muted markdown query--description">
            {markdown.toHTML(widget.getQuery().description || '')}
          </HtmlContent>
        </div>
      </div>
      {!isEmpty(parameters) && (
        <div className="m-b-10">
          <Parameters parameters={parameters} onValuesChange={onParametersUpdate} />
        </div>
      )}
    </>
{'fraction_non_alphanumeric': 0.13823529411764707, 'fraction_numerical': 0.0010230179028132991, 'mean_word_length': 3.9096045197740112, 'pattern_counts': {'":': 0, '<': 75, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4941961', 'n_tokens_mistral': 2196, 'n_tokens_neox': 2173, 'n_words': 470}