-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #34: add missing webcomponents
- Loading branch information
1 parent
b597b8c
commit 52442b4
Showing
2 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import { css } from '@emotion/css'; | ||
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; | ||
import { usePrevious } from 'react-use'; | ||
|
||
import { DataSourceInstanceSettings, VariableSuggestion } from '@grafana/data'; | ||
import { | ||
Button, | ||
DataLinkInput, | ||
InlineField, | ||
InlineSwitch, | ||
InlineFieldRow, | ||
InlineLabel, | ||
Input, | ||
useStyles2 | ||
} from '@grafana/ui'; | ||
|
||
import { DataSourcePicker } from '@grafana/runtime' | ||
|
||
import { DataLinkConfig } from '../types'; | ||
|
||
interface Props { | ||
value: DataLinkConfig; | ||
onChange: (value: DataLinkConfig) => void; | ||
onDelete: () => void; | ||
suggestions: VariableSuggestion[]; | ||
className?: string; | ||
} | ||
|
||
export const DataLink = (props: Props) => { | ||
const { value, onChange, onDelete, suggestions, className } = props; | ||
const styles = useStyles2(getStyles); | ||
const [showInternalLink, setShowInternalLink] = useInternalLink(value.datasourceUid); | ||
|
||
const handleChange = (field: keyof typeof value) => (event: React.ChangeEvent<HTMLInputElement>) => { | ||
onChange({ | ||
...value, | ||
[field]: event.currentTarget.value, | ||
}); | ||
}; | ||
|
||
return ( | ||
<div className={className}> | ||
<div className={styles.firstRow}> | ||
<InlineField | ||
label="Field" | ||
htmlFor="elasticsearch-datasource-config-field" | ||
labelWidth={12} | ||
tooltip={'Can be exact field name or a regex pattern that will match on the field name.'} | ||
> | ||
<Input | ||
type="text" | ||
id="elasticsearch-datasource-config-field" | ||
value={value.field} | ||
onChange={handleChange('field')} | ||
width={100} | ||
/> | ||
</InlineField> | ||
<Button | ||
variant={'destructive'} | ||
title="Remove field" | ||
icon="times" | ||
onClick={(event) => { | ||
event.preventDefault(); | ||
onDelete(); | ||
}} | ||
/> | ||
</div> | ||
|
||
<InlineFieldRow> | ||
<div className={styles.urlField}> | ||
<InlineLabel htmlFor="elasticsearch-datasource-internal-link" width={12}> | ||
{showInternalLink ? 'Query' : 'URL'} | ||
</InlineLabel> | ||
<DataLinkInput | ||
placeholder={showInternalLink ? '${__value.raw}' : 'http://example.com/${__value.raw}'} | ||
value={value.url || ''} | ||
onChange={(newValue) => | ||
onChange({ | ||
...value, | ||
url: newValue, | ||
}) | ||
} | ||
suggestions={suggestions} | ||
/> | ||
</div> | ||
|
||
<div className={styles.urlDisplayLabelField}> | ||
<InlineField | ||
label="URL Label" | ||
htmlFor="elasticsearch-datasource-url-label" | ||
labelWidth={14} | ||
tooltip={'Use to override the button label.'} | ||
> | ||
<Input | ||
type="text" | ||
id="elasticsearch-datasource-url-label" | ||
value={value.urlDisplayLabel} | ||
onChange={handleChange('urlDisplayLabel')} | ||
/> | ||
</InlineField> | ||
</div> | ||
</InlineFieldRow> | ||
|
||
<div className={styles.row}> | ||
<InlineField label="Internal link" labelWidth={12}> | ||
<InlineSwitch | ||
label="Internal link" | ||
value={showInternalLink || false} | ||
onChange={() => { | ||
if (showInternalLink) { | ||
onChange({ | ||
...value, | ||
datasourceUid: undefined, | ||
}); | ||
} | ||
setShowInternalLink(!showInternalLink); | ||
}} | ||
/> | ||
</InlineField> | ||
|
||
{showInternalLink && ( | ||
<DataSourcePicker | ||
tracing={true} | ||
onChange={(ds: DataSourceInstanceSettings) => { | ||
onChange({ | ||
...value, | ||
datasourceUid: ds.uid, | ||
}); | ||
}} | ||
current={value.datasourceUid} | ||
/> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
function useInternalLink(datasourceUid?: string): [boolean, Dispatch<SetStateAction<boolean>>] { | ||
const [showInternalLink, setShowInternalLink] = useState<boolean>(!!datasourceUid); | ||
const previousUid = usePrevious(datasourceUid); | ||
|
||
// Force internal link visibility change if uid changed outside of this component. | ||
useEffect(() => { | ||
if (!previousUid && datasourceUid && !showInternalLink) { | ||
setShowInternalLink(true); | ||
} | ||
if (previousUid && !datasourceUid && showInternalLink) { | ||
setShowInternalLink(false); | ||
} | ||
}, [previousUid, datasourceUid, showInternalLink]); | ||
|
||
return [showInternalLink, setShowInternalLink]; | ||
} | ||
|
||
const getStyles = () => ({ | ||
firstRow: css` | ||
display: flex; | ||
`, | ||
nameField: css` | ||
flex: 2; | ||
`, | ||
regexField: css` | ||
flex: 3; | ||
`, | ||
row: css` | ||
display: flex; | ||
align-items: baseline; | ||
`, | ||
urlField: css` | ||
display: flex; | ||
flex: 1; | ||
`, | ||
urlDisplayLabelField: css` | ||
flex: 1; | ||
`, | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { css } from '@emotion/css'; | ||
import React from 'react'; | ||
|
||
import { GrafanaTheme2, VariableOrigin, DataLinkBuiltInVars } from '@grafana/data'; | ||
import { ConfigSubSection } from '@grafana/experimental'; | ||
import { Button, useStyles2 } from '@grafana/ui'; | ||
|
||
import { DataLinkConfig } from '../types'; | ||
|
||
import { DataLink } from './DataLink'; | ||
|
||
const getStyles = (theme: GrafanaTheme2) => { | ||
return { | ||
addButton: css` | ||
margin-right: 10px; | ||
`, | ||
container: css` | ||
margin-bottom: ${theme.spacing(2)}; | ||
`, | ||
dataLink: css` | ||
margin-bottom: ${theme.spacing(1)}; | ||
`, | ||
}; | ||
}; | ||
|
||
export type Props = { | ||
value?: DataLinkConfig[]; | ||
onChange: (value: DataLinkConfig[]) => void; | ||
}; | ||
export const DataLinks = (props: Props) => { | ||
const { value, onChange } = props; | ||
const styles = useStyles2(getStyles); | ||
|
||
return ( | ||
<ConfigSubSection | ||
title="Data links" | ||
description="Add links to existing fields. Links will be shown in log row details next to the field value." | ||
> | ||
<div className={styles.container}> | ||
{value && value.length > 0 && ( | ||
<div className="gf-form-group"> | ||
{value.map((field, index) => { | ||
return ( | ||
<DataLink | ||
className={styles.dataLink} | ||
key={index} | ||
value={field} | ||
onChange={(newField) => { | ||
const newDataLinks = [...value]; | ||
newDataLinks.splice(index, 1, newField); | ||
onChange(newDataLinks); | ||
}} | ||
onDelete={() => { | ||
const newDataLinks = [...value]; | ||
newDataLinks.splice(index, 1); | ||
onChange(newDataLinks); | ||
}} | ||
suggestions={[ | ||
{ | ||
value: DataLinkBuiltInVars.valueRaw, | ||
label: 'Raw value', | ||
documentation: 'Raw value of the field', | ||
origin: VariableOrigin.Value, | ||
}, | ||
]} | ||
/> | ||
); | ||
})} | ||
</div> | ||
)} | ||
|
||
<Button | ||
type="button" | ||
variant={'secondary'} | ||
className={styles.addButton} | ||
icon="plus" | ||
onClick={(event) => { | ||
event.preventDefault(); | ||
const newDataLinks = [...(value || []), { field: '', url: '' }]; | ||
onChange(newDataLinks); | ||
}} | ||
> | ||
Add | ||
</Button> | ||
</div> | ||
</ConfigSubSection> | ||
); | ||
}; |