Code Insights: Reuse series picker in compute insight creation UI page (#38183)

* Improve type exports

* Refactor form series component (preparation for reusing it in compute insight page)

* Update repositories prop comment

* Put validators in a separate file

* Fix autofocus problem for series form fields

* Move insight series picker to the shared components directory

* Fix imports for the search-based and capture group insight creation UI pages

* Move constants to the top level exports

* Reuse form series component on the compute-powered insight page

* Simplify useField generics types

* Fix visual bug with query input editor

* Fix problems with bad imports and outdated mocks
This commit is contained in:
Vova Kulikov 2022-07-06 12:41:19 -04:00 committed by GitHub
parent 97ba145d8e
commit a79eba4791
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 401 additions and 386 deletions

View File

@ -1,7 +1,7 @@
import React from 'react'
import { Page } from '../../../../components/Page'
import { useUiFeatures } from '../../hooks/use-ui-features'
import { useUiFeatures } from '../../hooks'
import { CodeInsightsLimitAccessBanner } from './limit-access-banner/CodeInsightsLimitAccessBanner'

View File

@ -0,0 +1,85 @@
import { FC, ReactNode } from 'react'
import classNames from 'classnames'
import { Button } from '@sourcegraph/wildcard'
import { useUiFeatures } from '../../../hooks'
import { LimitedAccessLabel, useFieldAPI } from '../../index'
import { FormSeriesInput } from './components/form-series-input/FormSeriesInput'
import { SeriesCard } from './components/series-card/SeriesCard'
import { EditableDataSeries } from './types'
import { useEditableSeries } from './use-editable-series'
import styles from './FormSeries.module.scss'
export interface FormSeriesProps {
seriesField: useFieldAPI<EditableDataSeries[]>
repositories: string
showValidationErrorsOnMount: boolean
/**
* This field is only needed for specifying a special compute-specific
* query field description when this component is used on the compute-powered insight.
* This prop should be removed when we will have a better form series management
* solution, see https://github.com/sourcegraph/sourcegraph/issues/38236
*/
queryFieldDescription?: ReactNode
}
export const FormSeries: FC<FormSeriesProps> = props => {
const { seriesField, showValidationErrorsOnMount, repositories, queryFieldDescription } = props
const { licensed } = useUiFeatures()
const { series, changeSeries, editRequest, editCommit, cancelEdit, deleteSeries } = useEditableSeries(seriesField)
return (
<ul data-testid="form-series" className="list-unstyled d-flex flex-column">
{series.map((line, index) =>
line.edit ? (
<FormSeriesInput
key={line.id}
series={line}
showValidationErrorsOnMount={showValidationErrorsOnMount}
index={index + 1}
cancel={series.length > 1}
autofocus={line.autofocus}
repositories={repositories}
queryFieldDescription={queryFieldDescription}
className={classNames('p-3', styles.formSeriesItem)}
onSubmit={editCommit}
onCancel={() => cancelEdit(line.id)}
onChange={(seriesValues, valid) => changeSeries(seriesValues, valid, index)}
/>
) : (
line && (
<SeriesCard
key={line.id}
disabled={index >= 10}
onEdit={() => editRequest(line.id)}
onRemove={() => deleteSeries(line.id)}
className={styles.formSeriesItem}
{...line}
/>
)
)
)}
{!licensed && (
<LimitedAccessLabel message="Unlock Code Insights for unlimited data series" className="mx-auto my-3" />
)}
<Button
data-testid="add-series-button"
type="button"
onClick={() => editRequest()}
variant="link"
disabled={!licensed ? series.length >= 10 : false}
className={classNames(styles.formSeriesItem, styles.formSeriesAddButton, 'p-3')}
>
+ Add another data series
</Button>
</ul>
)
}

View File

@ -6,7 +6,7 @@ import { noop } from 'rxjs'
import { Label } from '@sourcegraph/wildcard'
import { DATA_SERIES_COLORS } from '../../constants'
import { DATA_SERIES_COLORS } from '../../../../../constants'
import styles from './FormColorInput.module.scss'

View File

@ -1,54 +1,42 @@
import React from 'react'
import { FC, ReactNode } from 'react'
import classNames from 'classnames'
import { noop } from 'rxjs'
import { Button, Card, Input, Code } from '@sourcegraph/wildcard'
import { getDefaultInputProps } from '../../../../../../components/form/getDefaultInputProps'
import { useField } from '../../../../../../components/form/hooks/useField'
import { useForm, ValidationResult } from '../../../../../../components/form/hooks/useForm'
import { InsightQueryInput } from '../../../../../../components/form/query-input/InsightQueryInput'
import { createRequiredValidator } from '../../../../../../components/form/validators'
import { searchQueryValidator } from '../../../capture-group/utils/search-query-validator'
import { DEFAULT_DATA_SERIES_COLOR } from '../../constants'
import { DEFAULT_DATA_SERIES_COLOR } from '../../../../../constants'
import { getDefaultInputProps, useField, InsightQueryInput, useForm } from '../../../../form'
import { EditableDataSeries } from '../../types'
import { FormColorInput } from '../form-color-input/FormColorInput'
import { getQueryPatternTypeFilter } from './get-pattern-type-filter'
const requiredNameField = createRequiredValidator('Name is a required field for data series.')
const validQuery = (value: string | undefined, validity: ValidityState | null | undefined): ValidationResult => {
const result = createRequiredValidator('Query is a required field for data series.')(value, validity)
if (result) {
return result
}
const { isNotContext, isNotRepo } = searchQueryValidator(value || '', true)
if (!isNotContext) {
return 'The `context:` filter is not supported; instead, run over all repositories and use the `context:` on the filter panel after creation'
}
if (!isNotRepo) {
return 'Do not include a `repo:` filter; add targeted repositories above, or filter repos on the filter panel after creation'
}
}
import { requiredNameField, validQuery } from './validators'
interface FormSeriesInputProps {
series: EditableDataSeries
/** Series index. */
index: number
/**
* Show all validation error of all fields within the form.
*/
/** Show all validation error of all fields within the form. */
showValidationErrorsOnMount?: boolean
series: EditableDataSeries
/** Code Insight repositories field string value - repo1, repo2, ... */
/**
* Code Insight repositories field string value - repo1, repo2, ...
* This prop is used in order to generate a proper link for the query preview button.
*/
repositories: string
/** Enable autofocus behavior of first input of form. */
/**
* This field is only needed for specifying a special compute-specific
* query field description when this component is used on the compute-powered insight.
* This prop should be removed when we will have a better form series management
* solution, see https://github.com/sourcegraph/sourcegraph/issues/38236
*/
queryFieldDescription?: ReactNode
/** Enable autofocus behavior of the first input element of series form. */
autofocus?: boolean
/** Enable cancel button. */
@ -67,7 +55,7 @@ interface FormSeriesInputProps {
onChange?: (formValues: EditableDataSeries, valid: boolean) => void
}
export const FormSeriesInput: React.FunctionComponent<React.PropsWithChildren<FormSeriesInputProps>> = props => {
export const FormSeriesInput: FC<FormSeriesInputProps> = props => {
const {
index,
series,
@ -76,6 +64,7 @@ export const FormSeriesInput: React.FunctionComponent<React.PropsWithChildren<Fo
cancel = false,
autofocus = true,
repositories,
queryFieldDescription,
onCancel = noop,
onSubmit = noop,
onChange = noop,
@ -147,7 +136,14 @@ export const FormSeriesInput: React.FunctionComponent<React.PropsWithChildren<Fo
repositories={repositories}
patternType={getQueryPatternTypeFilter(queryField.input.value)}
placeholder="Example: patternType:regexp const\s\w+:\s(React\.)?FunctionComponent"
message={<QueryFieldDescription />}
message={
queryFieldDescription ?? (
<span>
Do not include the <Code>context:</Code> or <Code>repo:</Code> filter; if needed,{' '}
<Code>repo:</Code> will be added automatically.
</span>
)
}
className="mt-4"
{...getDefaultInputProps(queryField)}
/>
@ -179,10 +175,3 @@ export const FormSeriesInput: React.FunctionComponent<React.PropsWithChildren<Fo
</Card>
)
}
const QueryFieldDescription: React.FunctionComponent<React.PropsWithChildren<unknown>> = () => (
<span>
Do not include the <Code>context:</Code> or <Code>repo:</Code> filter; if needed, <Code>repo:</Code> will be
added automatically.
</span>
)

View File

@ -0,0 +1,22 @@
import { searchQueryValidator } from '../../../../../pages/insights/creation/capture-group/utils/search-query-validator'
import { createRequiredValidator, ValidationResult } from '../../../../form'
export const requiredNameField = createRequiredValidator('Name is a required field for data series.')
export const validQuery = (value: string | undefined, validity: ValidityState | null | undefined): ValidationResult => {
const result = createRequiredValidator('Query is a required field for data series.')(value, validity)
if (result) {
return result
}
const { isNotContext, isNotRepo } = searchQueryValidator(value || '', true)
if (!isNotContext) {
return 'The `context:` filter is not supported; instead, run over all repositories and use the `context:` on the filter panel after creation'
}
if (!isNotRepo) {
return 'Do not include a `repo:` filter; add targeted repositories above, or filter repos on the filter panel after creation'
}
}

View File

@ -4,7 +4,7 @@ import classNames from 'classnames'
import { Button, Card } from '@sourcegraph/wildcard'
import { DEFAULT_DATA_SERIES_COLOR } from '../../../../constants'
import { DEFAULT_DATA_SERIES_COLOR } from '../../../../../constants'
import styles from './SeriesCard.module.scss'

View File

@ -0,0 +1,3 @@
export { FormSeries } from './FormSeries'
export { createDefaultEditSeries } from './use-editable-series'
export type { EditableDataSeries } from './types'

View File

@ -0,0 +1,7 @@
import { SearchBasedInsightSeries } from '../../../core'
export interface EditableDataSeries extends SearchBasedInsightSeries {
valid: boolean
edit: boolean
autofocus: boolean
}

View File

@ -2,61 +2,63 @@ import { useState } from 'react'
import * as uuid from 'uuid'
import { useFieldAPI } from '../../../../../../../components/form/hooks/useField'
import { DEFAULT_DATA_SERIES_COLOR } from '../../../constants'
import { CreateInsightFormFields, EditableDataSeries } from '../../../types'
import { useFieldAPI } from '../../form'
import { remove, replace } from './helpers'
const EDIT_SERIES_PREFIX = 'runtime-series'
import { EditableDataSeries } from './types'
export const createDefaultEditSeries = (series?: Partial<EditableDataSeries>): EditableDataSeries => ({
id: `${EDIT_SERIES_PREFIX}.${uuid.v4()}`,
...defaultEditSeries,
id: `runtime-series.${uuid.v4()}`,
...DEFAULT_EDITABLE_SERIES,
...series,
})
const defaultEditSeries = {
const DEFAULT_EDITABLE_SERIES = {
valid: false,
edit: false,
autofocus: false,
name: '',
query: '',
stroke: DEFAULT_DATA_SERIES_COLOR,
}
export interface UseEditableSeriesProps {
series: useFieldAPI<CreateInsightFormFields['series']>
}
export interface UseEditableSeriesAPI {
/**
* Edit series array used below for rendering series edit form.
* In case of some element has undefined value we're showing
* series card with data instead of form.
* */
editSeries: CreateInsightFormFields['series']
* A list of editable data series. Basically, this is just a
* sorted/filtered list of original data series but with
* additional logic around create/updated/delete actions for
* series list.
*/
series: EditableDataSeries[]
/**
* Handler to listen latest values of particular sereis form.
* */
listen: (liveSeries: EditableDataSeries, valid: boolean, index: number) => void
/** Call whenever the user changes any fields (title, query, color) of series */
changeSeries: (liveSeries: EditableDataSeries, valid: boolean, index: number) => void
/**
* Handlers for CRUD operations over series.
* */
/** Call whenever the user clicks the edit series button in series preview card. */
editRequest: (seriesId?: string) => void
/** Call whenever the user clicks the save series button */
editCommit: (editedSeries: EditableDataSeries) => void
/**
* Call whenever the user cancel series editing by clicking cancel button
* in series form.
*/
cancelEdit: (seriesId: string) => void
/**
* Call whenever the user tries to delete series by clicking delete button
* in series preview card.
*/
deleteSeries: (series: string) => void
}
/**
* Implementation of CRUD operation over insight series. Used in form to manage
* edit, delete, add, and cancel series forms.
* Basically this is just a stateful selector function over series that simplifies work
* with editable series and its special UX actions like delete series through preview card,
* edit series through form, create new series through add more series button.
*/
export function useEditableSeries(props: UseEditableSeriesProps): UseEditableSeriesAPI {
const { series } = props
export function useEditableSeries(series: useFieldAPI<EditableDataSeries[]>): UseEditableSeriesAPI {
const [seriesBeforeEdit, setSeriesBeforeEdit] = useState<Record<string, EditableDataSeries>>({})
const handleSeriesLiveChange = (liveSeries: EditableDataSeries, valid: boolean): void => {
@ -79,7 +81,7 @@ export function useEditableSeries(props: UseEditableSeriesProps): UseEditableSer
const index = newEditSeries.findIndex(series => series.id === seriesId)
if (index !== -1) {
newEditSeries[index] = { ...seriesValue[index], edit: true }
newEditSeries[index] = { ...seriesValue[index], edit: true, autofocus: true }
const newSeriesID = newEditSeries[index].id
@ -92,7 +94,7 @@ export function useEditableSeries(props: UseEditableSeriesProps): UseEditableSer
})
}
} else {
newEditSeries.push(createDefaultEditSeries({ edit: true }))
newEditSeries.push(createDefaultEditSeries({ edit: true, autofocus: true }))
}
series.meta.setState(state => ({ ...state, value: newEditSeries }))
@ -163,11 +165,21 @@ export function useEditableSeries(props: UseEditableSeriesProps): UseEditableSer
}
return {
editSeries: series.input.value,
listen: handleSeriesLiveChange,
series: series.input.value,
changeSeries: handleSeriesLiveChange,
editRequest: handleEditSeriesRequest,
editCommit: handleEditSeriesCommit,
cancelEdit: handleEditSeriesCancel,
deleteSeries: handleRemoveSeries,
}
}
/** Helper replace element in array by index and return new array. */
function replace<Element>(list: Element[], index: number, newElement: Element): Element[] {
return [...list.slice(0, index), newElement, ...list.slice(index + 1)]
}
/** Helper remove element from array by index. */
function remove<Element>(list: Element[], index: number): Element[] {
return [...list.slice(0, index), ...list.slice(index + 1)]
}

View File

@ -5,3 +5,5 @@ export { getSanitizedRepositories } from './sanitizers/repositories'
export { CodeInsightDashboardsVisibility } from './CodeInsightDashboardsVisibility'
export { CodeInsightTimeStepPicker } from './code-insight-time-step-picker/CodeInsightTimeStepPicker'
export { FormSeries, createDefaultEditSeries } from './form-series'
export type { EditableDataSeries } from './form-series'

View File

@ -25,7 +25,7 @@ export interface Validators<FieldValue> {
/**
* Subset of native input props that useField can set to the native input element.
*/
interface InputProps<Value> extends Omit<InputHTMLAttributes<HTMLInputElement>, 'name' | 'value' | 'onChange'> {
export interface InputProps<Value> extends Omit<InputHTMLAttributes<HTMLInputElement>, 'name' | 'value' | 'onChange'> {
onChange?: (value: Value) => void
}
@ -75,7 +75,7 @@ export type UseFieldProps<FormValues, Key, Value> = {
*
* Should be used with useForm hook to connect field and form component's states.
*/
export function useField<FormValues, Key extends keyof FormAPI<FormValues>['initialValues']>(
export function useField<FormValues, Key extends keyof FormValues>(
props: UseFieldProps<FormValues, Key, FormValues[Key]>
): useFieldAPI<FormValues[Key]> {
const { formApi, name, validators, onChange = noop, ...inputProps } = props
@ -130,7 +130,7 @@ export function useField<FormValues, Key extends keyof FormAPI<FormValues>['init
}
if (async) {
// Due the call of start async validation in useLayoutEffect we have to
// Due to the call of start async validation in useLayoutEffect we have to
// schedule the async validation event in the next tick to be able run
// observable pipeline validation since useAsyncValidation hook use
// useObservable hook internally which calls '.subscribe' in useEffect.
@ -189,7 +189,7 @@ export function useField<FormValues, Key extends keyof FormAPI<FormValues>['init
type FieldStateTransformer<Value> = (previousState: FieldState<Value>) => FieldState<Value>
function useFormFieldState<FormValues, Key extends keyof FormAPI<FormValues>['initialValues']>(
function useFormFieldState<FormValues, Key extends keyof FormValues>(
name: Key,
formAPI: FormAPI<FormValues>
): [FieldState<FormValues[Key]>, Dispatch<FieldStateTransformer<FormValues[Key]>>] {

View File

@ -15,7 +15,7 @@ import {
import { debounce, DebouncedFunc, isFunction } from 'lodash'
import { noop } from 'rxjs'
import { useDistinctValue } from '../../../hooks/use-distinct-value'
import { useDistinctValue } from '../../../hooks'
// Special key for the submit error store.
export const FORM_ERROR = 'useForm/submissionErrors'
@ -106,7 +106,7 @@ export interface FormAPI<FormValues> {
/**
* Mark to understand was there an attempt by user to submit the form?
* Used in useField hook to trigger appearance of error message if
* user tried submit the form.
* user tried to submit the form.
*/
submitted: boolean

View File

@ -11,8 +11,10 @@ export { FormGroup } from './form-group/FormGroup'
// form hooks
export { useForm, FORM_ERROR } from './hooks/useForm'
export type { Form, SubmissionErrors, FormChangeEvent } from './hooks/useForm'
export type { Form, ValidationResult, SubmissionErrors, FormChangeEvent } from './hooks/useForm'
export { useField } from './hooks/useField'
export type { useFieldAPI } from './hooks/useField'
export { useCheckboxes } from './hooks/useCheckboxes'
export { useAsyncInsightTitleValidator } from './hooks/use-async-insight-title-validator'

View File

@ -5,6 +5,11 @@
}
.input-wrapper {
// Spread standard input paddings in order to fix visually problem
// with codemirror editor on the code insight creation UI pages.
// See https://github.com/sourcegraph/sourcegraph/issues/37785
padding-top: 0.5rem;
padding-bottom: 0.5rem;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

View File

@ -34,7 +34,7 @@ export const InsightQueryInput = forwardRef<HTMLInputElement, InsightQueryInputP
{children}
</Monaco.Root>
) : (
<Monaco.Field {...props} ref={reference} className={props.className} />
<Monaco.Field {...props} ref={reference} className={classNames(styles.inputWrapper, props.className)} />
)}
<Monaco.PreviewLink query={previewQuery} patternType={patternType} className={styles.previewButton} />

View File

@ -1,6 +1,6 @@
import escapeRegExp from 'lodash/escapeRegExp'
import { getSanitizedRepositories } from '../../../creation-ui-kit'
import { getSanitizedRepositories } from '../../../creation-ui'
export const generateRepoFiltersQuery = (repositoriesString: string): string => {
const repositories = getSanitizedRepositories(repositoriesString)

View File

@ -4,7 +4,7 @@ import { Combobox, ComboboxInput, ComboboxPopover } from '@reach/combobox'
import { FlexTextArea } from '@sourcegraph/wildcard'
import { getSanitizedRepositories } from '../../creation-ui-kit'
import { getSanitizedRepositories } from '../../creation-ui'
import { SuggestionsPanel } from './components/suggestion-panel/SuggestionPanel'
import { useRepoSuggestions } from './hooks/use-repo-suggestions'

View File

@ -1,4 +1,4 @@
import { getSanitizedRepositories } from '../../../creation-ui-kit'
import { getSanitizedRepositories } from '../../../creation-ui'
interface SuggestionsSearchTermInput {
value: string

View File

@ -2,9 +2,9 @@ import { Validator } from './hooks/useField'
import { ValidationResult } from './hooks/useForm'
/**
* Validator for required form field which returns error massage
* Validator for required form field which returns error message
* as a sign of invalid state.
* */
*/
export const createRequiredValidator = <Value>(errorMessage: string): Validator<Value> => (value, validity) => {
if (validity?.valueMissing) {
return errorMessage
@ -20,6 +20,6 @@ export const createRequiredValidator = <Value>(errorMessage: string): Validator<
/**
* Composes a few validators together and show first error for form field.
* */
*/
export const composeValidators = <Value>(...validators: Validator<Value>[]): Validator<Value> => (value, validity) =>
validators.reduce<ValidationResult>((error, validator) => error || validator(value, validity), undefined)

View File

@ -3,7 +3,7 @@ export { CodeInsightsIcon } from '../../../insights/Icons'
export * from './insights-view-grid'
export * from './views'
export * from './trancated-text/TruncatedText'
export * from './creation-ui-kit'
export * from './creation-ui'
export * from './form'
export * from './limited-access-label/LimitedAccessLabel'
export * from './code-insights-page/CodeInsightsPage'

View File

@ -9,8 +9,7 @@ import { Button, Menu, MenuButton, MenuItem, MenuList, H2 } from '@sourcegraph/w
import { getLineColor, LegendItem, LegendList, ParentSize, Series } from '../../../../../charts'
import { WebStory } from '../../../../../components/WebStory'
import { useSeriesToggle } from '../../../../../insights/utils/use-series-toggle'
import { SeriesChart } from '../chart'
import { SeriesBasedChartTypes } from '../types'
import { SeriesBasedChartTypes, SeriesChart } from '../chart'
import * as Card from './InsightCard'

View File

@ -1,9 +1,8 @@
import { Meta, Story } from '@storybook/react'
import { WebStory } from '../../../../../../components/WebStory'
import { CategoricalBasedChartTypes } from '../../types'
import { CategoricalChart } from './CategoricalChart'
import { CategoricalBasedChartTypes, CategoricalChart } from './CategoricalChart'
const StoryConfig: Meta = {
title: 'web/insights/views/CategoricalChart',

View File

@ -1,9 +1,13 @@
import React, { SVGProps } from 'react'
import { CategoricalLikeChart, PieChart } from '../../../../../../charts'
import { CategoricalBasedChartTypes } from '../../types'
import { LockedChart } from '../locked/LockedChart'
export enum CategoricalBasedChartTypes {
Pie,
Bar,
}
export interface CategoricalChartProps<Datum>
extends CategoricalLikeChart<Datum>,
Omit<SVGProps<SVGSVGElement>, 'type'> {

View File

@ -1,5 +1,5 @@
export { SeriesChart } from './series/SeriesChart'
export { CategoricalChart } from './categorical/CategoricalChart'
export { SeriesChart, SeriesBasedChartTypes } from './series/SeriesChart'
export { CategoricalChart, CategoricalBasedChartTypes } from './categorical/CategoricalChart'
export type { SeriesChartProps } from './series/SeriesChart'
export type { CategoricalChartProps } from './categorical/CategoricalChart'

View File

@ -3,9 +3,8 @@ import { Meta, Story } from '@storybook/react'
import { Series } from '../../../../../../charts'
import { WebStory } from '../../../../../../components/WebStory'
import { useSeriesToggle } from '../../../../../../insights/utils/use-series-toggle'
import { SeriesBasedChartTypes } from '../../types'
import { SeriesChart } from './SeriesChart'
import { SeriesBasedChartTypes, SeriesChart } from './SeriesChart'
const StoryConfig: Meta = {
title: 'web/insights/views/SeriesChart',

View File

@ -4,9 +4,12 @@ import { LineChart, SeriesLikeChart } from '../../../../../../charts'
import { LineChartProps } from '../../../../../../charts/components/line-chart/LineChart'
import { SeriesWithData } from '../../../../../../charts/components/line-chart/utils'
import { UseSeriesToggleReturn } from '../../../../../../insights/utils/use-series-toggle'
import { SeriesBasedChartTypes } from '../../types'
import { LockedChart } from '../locked/LockedChart'
export enum SeriesBasedChartTypes {
Line,
}
export interface SeriesChartProps<D> extends SeriesLikeChart<D>, Omit<SVGProps<SVGSVGElement>, 'type'> {
type: SeriesBasedChartTypes
width: number

View File

@ -1,6 +1,5 @@
export * from './card/InsightCard'
export { SeriesChart, CategoricalChart } from './chart'
export { CategoricalBasedChartTypes, SeriesBasedChartTypes } from './types'
export { SeriesChart, CategoricalChart, CategoricalBasedChartTypes, SeriesBasedChartTypes } from './chart'
export type { SeriesChartProps, CategoricalChartProps } from './chart'

View File

@ -1,8 +0,0 @@
export enum SeriesBasedChartTypes {
Line,
}
export enum CategoricalBasedChartTypes {
Pie,
Bar,
}

View File

@ -1,5 +1,5 @@
import { InsightDataNode } from '../../../../../../../graphql-operations'
import { DATA_SERIES_COLORS } from '../../../../../pages/insights/creation/search-insight'
import { DATA_SERIES_COLORS } from '../../../../../constants'
import { BackendInsight } from '../../../../types'
import { BackendInsightData } from '../../../code-insights-backend-types'
import { createLineChartContent } from '../../../utils/create-line-chart-content'

View File

@ -1,5 +1,6 @@
export { useLazyParallelRequest } from './use-parallel-requests/use-parallel-request'
export type { LazyQueryState, LazyQueryResult } from './use-parallel-requests/use-parallel-request'
export { useApi } from './use-api'
export { useCopyURLHandler } from './use-copy-url-handler'
export { useDeleteInsight } from './use-delete-insight'

View File

@ -24,6 +24,13 @@ import { CodeInsightsBackendContext, SeriesChartContent } from '../../../core'
import { getSanitizedCaptureQuery } from './capture-group/utils/capture-group-insight-sanitizer'
import { InsightStep } from './search-insight'
export interface LivePreviewSeries {
query: string
label: string
generatedFromCaptureGroup: boolean
stroke: string
}
interface LineChartLivePreviewProps {
disabled: boolean
repositories: string
@ -31,12 +38,7 @@ interface LineChartLivePreviewProps {
step: InsightStep
isAllReposMode: boolean
className?: string
series: {
query: string
label: string
generatedFromCaptureGroup: boolean
stroke: string
}[]
series: LivePreviewSeries[]
}
export const LineChartLivePreview: React.FunctionComponent<

View File

@ -2,7 +2,7 @@ import { FilterType, resolveFilter } from '@sourcegraph/shared/src/search/query/
import { scanSearchQuery } from '@sourcegraph/shared/src/search/query/scanner'
import { Filter } from '@sourcegraph/shared/src/search/query/token'
import { getSanitizedRepositories } from '../../../../../components/creation-ui-kit'
import { getSanitizedRepositories } from '../../../../../components'
import { InsightExecutionType, InsightType, MinimalCaptureGroupInsightData } from '../../../../../core'
import { CaptureGroupFormFields } from '../types'

View File

@ -6,8 +6,7 @@ import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryServi
import { Link, PageHeader, Text, useLocalStorage } from '@sourcegraph/wildcard'
import { PageTitle } from '../../../../../../components/PageTitle'
import { CodeInsightsPage } from '../../../../components/code-insights-page/CodeInsightsPage'
import { FormChangeEvent } from '../../../../components/form/hooks/useForm'
import { CodeInsightsPage, FormChangeEvent } from '../../../../components'
import { ComputeInsightCreationContent } from './components/ComputeInsightCreationContent'
import { CreateComputeInsightFormFields } from './components/types'
@ -42,7 +41,7 @@ export const ComputeInsightCreationPage: FunctionComponent<ComputeInsightCreatio
}
return (
<CodeInsightsPage className="col-10">
<CodeInsightsPage className="col-12">
<PageTitle title="Create compute insight - Code Insights" />
<PageHeader

View File

@ -1,13 +1,34 @@
import { FunctionComponent, HTMLAttributes } from 'react'
import { CreationUiLayout, CreationUIForm, CreationUIPreview } from '../../../../../components'
import { FormChangeEvent, SubmissionErrors } from '../../../../../components/form/hooks/useForm'
import { Code } from '@sourcegraph/wildcard'
import {
CreationUiLayout,
CreationUIForm,
CreationUIPreview,
FormChangeEvent,
SubmissionErrors,
FormSeries,
FormGroup,
useForm,
createDefaultEditSeries,
useField,
} from '../../../../../components'
import { useUiFeatures } from '../../../../../hooks'
import { CreateComputeInsightFormFields } from './types'
const INITIAL_INSIGHT_VALUES: CreateComputeInsightFormFields = {
series: [createDefaultEditSeries({ edit: true })],
title: '',
repositories: '',
dashboardReferenceCount: 0,
}
type NativeContainerProps = Omit<HTMLAttributes<HTMLDivElement>, 'onSubmit' | 'onChange'>
interface ComputeInsightCreationContentProps extends NativeContainerProps {
mode?: 'creation' | 'edit'
initialValue?: Partial<CreateComputeInsightFormFields>
onChange: (event: FormChangeEvent<CreateComputeInsightFormFields>) => void
@ -16,11 +37,55 @@ interface ComputeInsightCreationContentProps extends NativeContainerProps {
}
export const ComputeInsightCreationContent: FunctionComponent<ComputeInsightCreationContentProps> = props => {
const { initialValue, onChange, onSubmit, onCancel, ...attributes } = props
const { mode = 'creation', initialValue, onChange, onSubmit, onCancel, ...attributes } = props
const { licensed } = useUiFeatures()
const form = useForm<CreateComputeInsightFormFields>({
initialValues: { ...INITIAL_INSIGHT_VALUES, ...initialValue },
onSubmit,
onChange,
touched: mode === 'edit',
})
const series = useField({
name: 'series',
formApi: form.formAPI,
})
return (
<CreationUiLayout {...attributes}>
<CreationUIForm>Hello World</CreationUIForm>
<CreationUIForm>
<FormGroup
name="data series group"
title="Data series"
subtitle={
licensed
? 'Add any number of data series to your chart'
: 'Add up to 10 data series to your chart'
}
innerRef={series.input.ref}
>
<FormSeries
seriesField={series}
repositories=""
showValidationErrorsOnMount={false}
queryFieldDescription={
<ul className="pl-3">
<li>
Do not include the <Code weight="bold">repo:</Code> filter as it will be added
automatically, if needed{' '}
</li>
<li>
You can use <Code weight="bold">before:</Code> and <Code weight="bold">after:</Code>{' '}
operators for <Code weight="bold">type:diff</Code> and{' '}
<Code weight="bold">type:commit</Code> to define the timeframe (example query:{' '}
<Code>type:diff author:nick before:"last thursday" SearchTerm</Code>)
</li>
</ul>
}
/>
</FormGroup>
</CreationUIForm>
<CreationUIPreview>This is live preview</CreationUIPreview>
</CreationUiLayout>

View File

@ -1,4 +1,4 @@
import { EditableDataSeries } from '../../search-insight'
import { EditableDataSeries } from '../../../../../components'
export interface CreateComputeInsightFormFields {
/**

View File

@ -6,7 +6,7 @@ import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/teleme
import { WebStory } from '../../../../../../components/WebStory'
import { CodeInsightsBackendStoryMock } from '../../../../CodeInsightsBackendStoryMock'
import { SERIES_MOCK_CHART } from '../../../../components/creation-ui-kit'
import { SERIES_MOCK_CHART } from '../../../../components'
import { SearchInsightCreationPage as SearchInsightCreationPageComponent } from './SearchInsightCreationPage'

View File

@ -1,10 +1,11 @@
import React, { FormEventHandler, RefObject, useMemo } from 'react'
import { FC, FormEventHandler, RefObject, useMemo } from 'react'
import { ErrorAlert } from '@sourcegraph/branded/src/components/alerts'
import { Button, Checkbox, Input, Link, useObservable } from '@sourcegraph/wildcard'
import { LoaderButton } from '../../../../../../../components/LoaderButton'
import {
FormSeries,
LimitedAccessLabel,
CodeInsightDashboardsVisibility,
CodeInsightTimeStepPicker,
@ -17,9 +18,7 @@ import {
} from '../../../../../components'
import { Insight } from '../../../../../core'
import { useUiFeatures } from '../../../../../hooks'
import { CreateInsightFormFields, EditableDataSeries } from '../types'
import { FormSeries } from './form-series/FormSeries'
import { CreateInsightFormFields } from '../types'
interface CreationSearchInsightFormProps {
/** This component might be used in edit or creation insight case. */
@ -44,22 +43,6 @@ interface CreationSearchInsightFormProps {
insight?: Insight
onCancel: () => void
/**
* Handler to listen latest value form particular series edit form
* Used to get information for live preview chart.
*/
onSeriesLiveChange: (liveSeries: EditableDataSeries, isValid: boolean, index: number) => void
/**
* Handlers for CRUD operation over series. Add, delete, update and cancel
* series edit form.
*/
onEditSeriesRequest: (seriesId?: string) => void
onEditSeriesCommit: (editedSeries: EditableDataSeries) => void
onEditSeriesCancel: (seriesId: string) => void
onSeriesRemove: (seriesId: string) => void
onFormReset: () => void
}
@ -67,9 +50,7 @@ interface CreationSearchInsightFormProps {
* Displays creation code insight form (title, visibility, series, etc.)
* UI layer only, all controlled data should be managed by consumer of this component.
*/
export const SearchInsightCreationForm: React.FunctionComponent<
React.PropsWithChildren<CreationSearchInsightFormProps>
> = props => {
export const SearchInsightCreationForm: FC<CreationSearchInsightFormProps> = props => {
const {
mode,
innerRef,
@ -88,11 +69,6 @@ export const SearchInsightCreationForm: React.FunctionComponent<
dashboardReferenceCount,
insight,
onCancel,
onSeriesLiveChange,
onEditSeriesRequest,
onEditSeriesCommit,
onEditSeriesCancel,
onSeriesRemove,
onFormReset,
} = props
@ -164,14 +140,9 @@ export const SearchInsightCreationForm: React.FunctionComponent<
innerRef={series.input.ref}
>
<FormSeries
series={series.input.value}
seriesField={series}
repositories={repositories.input.value}
showValidationErrorsOnMount={submitted}
onLiveChange={onSeriesLiveChange}
onEditSeriesRequest={onEditSeriesRequest}
onEditSeriesCommit={onEditSeriesCommit}
onEditSeriesCancel={onEditSeriesCancel}
onSeriesRemove={onSeriesRemove}
/>
</FormGroup>

View File

@ -1,128 +0,0 @@
import React from 'react'
import classNames from 'classnames'
import { Button } from '@sourcegraph/wildcard'
import { LimitedAccessLabel } from '../../../../../../components/limited-access-label/LimitedAccessLabel'
import { useUiFeatures } from '../../../../../../hooks/use-ui-features'
import { EditableDataSeries } from '../../types'
import { FormSeriesInput } from '../form-series-input/FormSeriesInput'
import { SeriesCard } from './components/series-card/SeriesCard'
import styles from './FormSeries.module.scss'
export interface FormSeriesProps {
/**
* Show all validation error for all forms and fields within the series forms.
*/
showValidationErrorsOnMount: boolean
/**
* Controlled value (series - chart lines) for series input component.
*/
series?: EditableDataSeries[]
/**
* Code Insight repositories field string value - repo1, repo2, ...
*/
repositories: string
/**
* Live change series handler while user typing in active series form.
* Used by consumers to get latest values from series inputs and pass
* them tp live preview chart.
*/
onLiveChange: (liveSeries: EditableDataSeries, isValid: boolean, index: number) => void
/**
* Handler that runs every time user clicked edit on particular
* series card.
*/
onEditSeriesRequest: (seriesId?: string) => void
/**
* Handler that runs every time use clicked commit (done) in
* series edit form.
*/
onEditSeriesCommit: (editedSeries: EditableDataSeries) => void
/**
* Handler that runs every time use canceled (click cancel) in
* series edit form.
*/
onEditSeriesCancel: (seriesId: string) => void
/**
* Handler that runs every time use removed (click remove) in
* series card.
*/
onSeriesRemove: (seriesId: string) => void
}
/**
* Renders form series (sub-form) for series (chart lines) creation code insight form.
*/
export const FormSeries: React.FunctionComponent<React.PropsWithChildren<FormSeriesProps>> = props => {
const {
series = [],
showValidationErrorsOnMount,
repositories,
onEditSeriesRequest,
onEditSeriesCommit,
onEditSeriesCancel,
onSeriesRemove,
onLiveChange,
} = props
const { licensed } = useUiFeatures()
return (
<ul data-testid="form-series" className="list-unstyled d-flex flex-column">
{series.map((line, index) =>
line.edit ? (
<FormSeriesInput
key={line.id}
series={line}
showValidationErrorsOnMount={showValidationErrorsOnMount}
index={index + 1}
cancel={series.length > 1}
autofocus={series.length > 1}
repositories={repositories}
onSubmit={onEditSeriesCommit}
onCancel={() => onEditSeriesCancel(line.id)}
className={classNames('p-3', styles.formSeriesItem)}
onChange={(seriesValues, valid) => onLiveChange({ ...line, ...seriesValues }, valid, index)}
/>
) : (
line && (
<SeriesCard
key={line.id}
disabled={index >= 10}
onEdit={() => onEditSeriesRequest(line.id)}
onRemove={() => onSeriesRemove(line.id)}
className={styles.formSeriesItem}
{...line}
/>
)
)
)}
{!licensed && (
<LimitedAccessLabel message="Unlock Code Insights for unlimited data series" className="mx-auto my-3" />
)}
<Button
data-testid="add-series-button"
type="button"
onClick={() => onEditSeriesRequest()}
variant="link"
disabled={!licensed ? series.length >= 10 : false}
className={classNames(styles.formSeriesItem, styles.formSeriesAddButton, 'p-3')}
>
+ Add another data series
</Button>
</ul>
)
}

View File

@ -8,15 +8,16 @@ import {
CreationUIPreview,
FormChangeEvent,
SubmissionErrors,
createDefaultEditSeries,
EditableDataSeries,
} from '../../../../../../components'
import { Insight } from '../../../../../../core'
import { LineChartLivePreview } from '../../../LineChartLivePreview'
import { CreateInsightFormFields, EditableDataSeries } from '../../types'
import { LineChartLivePreview, LivePreviewSeries } from '../../../LineChartLivePreview'
import { CreateInsightFormFields } from '../../types'
import { getSanitizedSeries } from '../../utils/insight-sanitizer'
import { SearchInsightCreationForm } from '../SearchInsightCreationForm'
import { useEditableSeries, createDefaultEditSeries } from './hooks/use-editable-series'
import { useInsightCreationForm } from './hooks/use-insight-creation-form/use-insight-creation-form'
import { useInsightCreationForm } from './hooks/use-insight-creation-form'
export interface SearchInsightCreationContentProps {
/** This component might be used in edit or creation insight case. */
@ -61,8 +62,6 @@ export const SearchInsightCreationContent: FC<SearchInsightCreationContentProps>
onSubmit,
})
const { editSeries, listen, editRequest, editCommit, cancelEdit, deleteSeries } = useEditableSeries({ series })
const handleFormReset = (): void => {
// TODO [VK] Change useForm API in order to implement form.reset method.
title.input.onChange('')
@ -78,7 +77,7 @@ export const SearchInsightCreationContent: FC<SearchInsightCreationContentProps>
// we should disable live chart preview
const allFieldsForPreviewAreValid =
repositories.meta.validState === 'VALID' &&
(series.meta.validState === 'VALID' || editSeries.some(series => series.valid)) &&
(series.meta.validState === 'VALID' || series.input.value.some(series => series.valid)) &&
stepValue.meta.validState === 'VALID' &&
// For the "all repositories" mode we are not able to show the live preview chart
!allReposMode.input.value
@ -107,12 +106,7 @@ export const SearchInsightCreationContent: FC<SearchInsightCreationContentProps>
isFormClearActive={hasFilledValue}
dashboardReferenceCount={initialValue?.dashboardReferenceCount}
insight={insight}
onSeriesLiveChange={listen}
onCancel={onCancel}
onEditSeriesRequest={editRequest}
onEditSeriesCancel={cancelEdit}
onEditSeriesCommit={editCommit}
onSeriesRemove={deleteSeries}
onFormReset={handleFormReset}
/>
@ -121,7 +115,7 @@ export const SearchInsightCreationContent: FC<SearchInsightCreationContentProps>
disabled={!allFieldsForPreviewAreValid}
repositories={repositories.meta.value}
isAllReposMode={allReposMode.input.value}
series={seriesToPreview(editSeries)}
series={seriesToPreview(series.input.value)}
step={step.meta.value}
stepValue={stepValue.meta.value}
/>
@ -129,7 +123,7 @@ export const SearchInsightCreationContent: FC<SearchInsightCreationContentProps>
)
}
function seriesToPreview(currentSeries: EditableDataSeries[]): any {
function seriesToPreview(currentSeries: EditableDataSeries[]): LivePreviewSeries[] {
const validSeries = currentSeries.filter(series => series.valid)
return getSanitizedSeries(validSeries).map(series => ({
query: series.query,

View File

@ -1,13 +0,0 @@
/**
* Helper replace element in array by index and return new array.
* */
export function replace<Element>(list: Element[], index: number, newElement: Element): Element[] {
return [...list.slice(0, index), newElement, ...list.slice(index + 1)]
}
/**
* Helper remove element from array by index
* */
export function remove<Element>(list: Element[], index: number): Element[] {
return [...list.slice(0, index), ...list.slice(index + 1)]
}

View File

@ -1,15 +1,22 @@
import { useAsyncInsightTitleValidator } from '../../../../../../../../components/form/hooks/use-async-insight-title-validator'
import { useField, useFieldAPI } from '../../../../../../../../components/form/hooks/useField'
import { Form, FormChangeEvent, SubmissionErrors, useForm } from '../../../../../../../../components/form/hooks/useForm'
import { createRequiredValidator } from '../../../../../../../../components/form/validators'
import { CreateInsightFormFields, EditableDataSeries, InsightStep } from '../../../../types'
import { INITIAL_INSIGHT_VALUES } from '../../initial-insight-values'
import {
useAsyncInsightTitleValidator,
useField,
useFieldAPI,
Form,
FormChangeEvent,
SubmissionErrors,
useForm,
createRequiredValidator,
EditableDataSeries,
} from '../../../../../../../components'
import { CreateInsightFormFields, InsightStep } from '../../../types'
import { INITIAL_INSIGHT_VALUES } from '../initial-insight-values'
import {
repositoriesExistValidator,
repositoriesFieldValidator,
requiredStepValueField,
seriesRequired,
} from '../../validators'
} from '../validators'
const titleRequiredValidator = createRequiredValidator('Title is a required field.')

View File

@ -1,7 +1,6 @@
import { createDefaultEditSeries } from '../../../../../../components'
import { CreateInsightFormFields } from '../../types'
import { createDefaultEditSeries } from './hooks/use-editable-series'
export const INITIAL_INSIGHT_VALUES: CreateInsightFormFields = {
// If user opens the creation form to create insight
// we want to show the series form as soon as possible

View File

@ -1,12 +1,10 @@
import { renderError } from '@sourcegraph/branded/src/components/alerts'
import { dedupeWhitespace } from '@sourcegraph/common'
import { getSanitizedRepositories } from '../../../../../../components/creation-ui-kit'
import { getSanitizedRepositories, createRequiredValidator, EditableDataSeries } from '../../../../../../components'
import { Validator } from '../../../../../../components/form/hooks/useField'
import { AsyncValidator } from '../../../../../../components/form/hooks/utils/use-async-validation'
import { createRequiredValidator } from '../../../../../../components/form/validators'
import { fetchRepositories } from '../../../../../../core/backend/gql-backend/methods/get-built-in-insight-data/utils/fetch-repositories'
import { EditableDataSeries } from '../../types'
export const repositoriesFieldValidator: Validator<string> = value => {
if (value !== undefined && dedupeWhitespace(value).trim() === '') {

View File

@ -1,8 +1,6 @@
export { DATA_SERIES_COLORS, DEFAULT_DATA_SERIES_COLOR } from './constants'
export { SearchInsightCreationPage } from './SearchInsightCreationPage'
export { encodeSearchInsightUrl } from './utils/search-insight-url-parsers/search-insight-url-parsers'
export { getQueryPatternTypeFilter } from './components/form-series-input/get-pattern-type-filter'
export { getQueryPatternTypeFilter } from '../../../../components/creation-ui/form-series/components/form-series-input/get-pattern-type-filter'
export type { SearchInsightURLValues } from './utils/search-insight-url-parsers/search-insight-url-parsers'
export type { CreateInsightFormFields, EditableDataSeries, InsightStep } from './types'
export type { CreateInsightFormFields, InsightStep } from './types'

View File

@ -1,36 +1,21 @@
import { SearchBasedInsightSeries } from '../../../../core'
import { EditableDataSeries } from '../../../../components/creation-ui/form-series/types'
export type InsightStep = 'hours' | 'days' | 'weeks' | 'months' | 'years'
export interface EditableDataSeries extends SearchBasedInsightSeries {
valid: boolean
edit: boolean
}
export interface CreateInsightFormFields {
/**
* Code Insight series setting (name of line, line query, color)
*/
/** Code Insight series setting (name of line, line query, color) */
series: EditableDataSeries[]
/**
* Title of code insight
*/
/** Title of code insight */
title: string
/**
* Repositories which to be used to get the info for code insights
*/
/** Repositories which to be used to get the info for code insights */
repositories: string
/**
* Setting for set chart step - how often do we collect data.
*/
/** Setting for set chart step - how often do we collect data. */
step: InsightStep
/**
* Value for insight step setting
*/
/** Value for insight step setting */
stepValue: string
/**
@ -39,8 +24,6 @@ export interface CreateInsightFormFields {
*/
allRepos: boolean
/**
* The total number of dashboards on which this insight is referenced.
*/
/** The total number of dashboards on which this insight is referenced. */
dashboardReferenceCount: number
}

View File

@ -1,13 +1,13 @@
import { getSanitizedRepositories } from '../../../../../components/creation-ui-kit'
import { getSanitizedRepositories } from '../../../../../components'
import {
MinimalSearchBasedInsightData,
InsightExecutionType,
InsightType,
SearchBasedInsightSeries,
} from '../../../../../core'
import { CreateInsightFormFields, EditableDataSeries } from '../types'
import { CreateInsightFormFields } from '../types'
export function getSanitizedLine(line: EditableDataSeries): SearchBasedInsightSeries {
export function getSanitizedLine(line: SearchBasedInsightSeries): SearchBasedInsightSeries {
return {
id: line.id,
name: line.name.trim(),
@ -20,7 +20,7 @@ export function getSanitizedLine(line: EditableDataSeries): SearchBasedInsightSe
}
}
export function getSanitizedSeries(rawSeries: EditableDataSeries[]): SearchBasedInsightSeries[] {
export function getSanitizedSeries(rawSeries: SearchBasedInsightSeries[]): SearchBasedInsightSeries[] {
return rawSeries.map(getSanitizedLine)
}

View File

@ -27,8 +27,8 @@ describe('decodeSearchInsightUrl', () => {
title: 'Insight title',
allRepos: true,
series: [
{ id: 1, edit: false, valid: true, name: 'series 1', query: 'test1', stroke: 'red' },
{ id: 2, edit: false, valid: true, name: 'series 2', query: 'test2', stroke: 'blue' },
{ id: 1, edit: false, valid: true, autofocus: false, name: 'series 1', query: 'test1', stroke: 'red' },
{ id: 2, edit: false, valid: true, autofocus: false, name: 'series 2', query: 'test2', stroke: 'blue' },
],
})
})
@ -51,8 +51,24 @@ describe('encodeSearchInsightUrl', () => {
title: 'Insight title',
allRepos: true,
series: [
{ id: '1', edit: false, valid: true, name: 'series 1', query: 'test1', stroke: 'red' },
{ id: '2', edit: false, valid: true, name: 'series 2', query: 'test2', stroke: 'blue' },
{
id: '1',
edit: false,
valid: true,
autofocus: false,
name: 'series 1',
query: 'test1',
stroke: 'red',
},
{
id: '2',
edit: false,
valid: true,
autofocus: false,
name: 'series 2',
query: 'test2',
stroke: 'blue',
},
],
})
})

View File

@ -1,5 +1,5 @@
import { createDefaultEditSeries } from '../../../../../../components'
import { SearchBasedInsightSeries } from '../../../../../../core'
import { createDefaultEditSeries } from '../../components/search-insight-creation-content/hooks/use-editable-series'
import { CreateInsightFormFields } from '../../types'
export function decodeSearchInsightUrl(queryParameters: string): Partial<CreateInsightFormFields> | null {

View File

@ -6,8 +6,8 @@ import { stringHuman } from '@sourcegraph/shared/src/search/query/printer'
import { scanSearchQuery } from '@sourcegraph/shared/src/search/query/scanner'
import { isFilterType, isRepoFilter } from '@sourcegraph/shared/src/search/query/validate'
import { CodeInsightsBackendContext } from '../../../../../../core/backend/code-insights-backend-context'
import { createDefaultEditSeries } from '../../components/search-insight-creation-content/hooks/use-editable-series'
import { createDefaultEditSeries } from '../../../../../../components'
import { CodeInsightsBackendContext } from '../../../../../../core'
import { INITIAL_INSIGHT_VALUES } from '../../components/search-insight-creation-content/initial-insight-values'
import { CreateInsightFormFields } from '../../types'

View File

@ -1,6 +1,6 @@
import React, { useMemo } from 'react'
import { SubmissionErrors } from '../../../../components/form/hooks/useForm'
import { SubmissionErrors } from '../../../../components'
import { MinimalCaptureGroupInsightData, CaptureGroupInsight } from '../../../../core'
import { CaptureGroupFormFields } from '../../creation/capture-group'
import { CaptureGroupCreationContent } from '../../creation/capture-group/components/CaptureGroupCreationContent'

View File

@ -1,9 +1,8 @@
import React, { useMemo } from 'react'
import { SubmissionErrors } from '../../../../components/form/hooks/useForm'
import { SubmissionErrors, createDefaultEditSeries } from '../../../../components'
import { MinimalSearchBasedInsightData, SearchBasedInsight } from '../../../../core'
import { CreateInsightFormFields, InsightStep } from '../../creation/search-insight'
import { createDefaultEditSeries } from '../../creation/search-insight/components/search-insight-creation-content/hooks/use-editable-series'
import { SearchInsightCreationContent } from '../../creation/search-insight/components/search-insight-creation-content/SearchInsightCreationContent'
import { getSanitizedSearchInsight } from '../../creation/search-insight/utils/insight-sanitizer'

View File

@ -1,4 +1,4 @@
import { DATA_SERIES_COLORS } from '../../../../insights/creation/search-insight'
import { DATA_SERIES_COLORS } from '../../../../../constants'
import { CaptureGroupExampleContent, SearchInsightExampleContent } from './types'

View File

@ -1,8 +1,9 @@
import { SettingsExperimentalFeatures } from '@sourcegraph/shared/src/schema/settings.schema'
import { DATA_SERIES_COLORS } from '../../../../../constants'
import { InsightType } from '../../../../../core'
import { CaptureInsightUrlValues } from '../../../../insights/creation/capture-group'
import { DATA_SERIES_COLORS, SearchInsightURLValues } from '../../../../insights/creation/search-insight'
import { SearchInsightURLValues } from '../../../../insights/creation/search-insight'
export interface TemplateSection {
title: string

View File

@ -7,11 +7,13 @@ import { noop } from 'rxjs'
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { Button, Card, Link, useObservable, useDebounce, Icon, Input, H2, H3, Text } from '@sourcegraph/wildcard'
import { getDefaultInputProps } from '../../../../../components/form/getDefaultInputProps'
import { useField } from '../../../../../components/form/hooks/useField'
import { useForm } from '../../../../../components/form/hooks/useForm'
import { InsightQueryInput } from '../../../../../components/form/query-input/InsightQueryInput'
import { RepositoriesField } from '../../../../../components/form/repositories-field/RepositoriesField'
import {
getDefaultInputProps,
useField,
useForm,
InsightQueryInput,
RepositoriesField,
} from '../../../../../components'
import { CodeInsightsBackendContext } from '../../../../../core'
import { getQueryPatternTypeFilter } from '../../../../insights/creation/search-insight'
import {

View File

@ -5,8 +5,9 @@ import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryServi
import { useDeepMemo } from '@sourcegraph/wildcard'
import { useSeriesToggle } from '../../../../../../../insights/utils/use-series-toggle'
import { SeriesBasedChartTypes, SeriesChart } from '../../../../../components'
import {
SeriesBasedChartTypes,
SeriesChart,
getSanitizedRepositories,
useLivePreview,
StateStatus,
@ -18,16 +19,14 @@ import {
LivePreviewBanner,
LivePreviewLegend,
SERIES_MOCK_CHART,
} from '../../../../../components/creation-ui-kit'
import { CodeInsightsBackendContext, SeriesChartContent } from '../../../../../core'
} from '../../../../../components'
import { DATA_SERIES_COLORS } from '../../../../../constants'
import { CodeInsightsBackendContext, SearchBasedInsightSeries, SeriesChartContent } from '../../../../../core'
import { CodeInsightTrackType, useCodeInsightViewPings } from '../../../../../pings'
import { DATA_SERIES_COLORS, EditableDataSeries } from '../../../../insights/creation/search-insight'
const createExampleDataSeries = (query: string): EditableDataSeries[] => [
const createExampleDataSeries = (query: string): SearchBasedInsightSeries[] => [
{
query,
valid: true,
edit: false,
id: '1',
name: 'TODOs',
stroke: DATA_SERIES_COLORS.ORANGE,