Skip to content

Commit 513a14a

Browse files
lhawkman27lhawkinsclaudejmeegan2
authored
Replace inline JSON template popover with wiki link (#325)
* #321 replace inline JSON template popover with wiki link Remove the embedded JSON config template, copy-to-clipboard logic, and Popover/Button/Typography imports. The info icon now links directly to the JSON Config Spec wiki page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * #321 add tooltip to JSON Config Spec wiki link icon Wrap the info icon in an Ant Design Tooltip so users see a hint before clicking through to the wiki page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * #321 add aria-label to JSON Config Spec wiki link for accessibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * #321 replace wiki link icon with visible [see docs] text Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: lhawkins <lhawkins@teamraft.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: James Meegan <58824024+jmeegan2@users.noreply.github.com> Co-authored-by: James <jmeegan@teamraft.com>
1 parent c1e5d58 commit 513a14a

1 file changed

Lines changed: 16 additions & 206 deletions

File tree

superset-frontend/plugins/geoset-map-chart/src/components/JsonEditorControl.tsx

Lines changed: 16 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,10 @@
33
import { useState, useEffect } from 'react';
44
import Editor from 'react-simple-code-editor';
55
import { styled, SupersetTheme, t } from '@superset-ui/core';
6-
import { Popover, Button, Typography } from 'antd';
7-
import {
8-
InfoCircleOutlined,
9-
CopyOutlined,
10-
CheckOutlined,
11-
} from '@ant-design/icons';
126
import Prism from 'prismjs';
137
import 'prismjs/components/prism-json';
148
import { prettyStringify } from '../utils/safeStringify';
159

16-
const { Text } = Typography;
17-
1810
const Container = styled.div(
1911
({ theme }: { theme: SupersetTheme }) => `
2012
font-family: ${theme.fontFamilyCode};
@@ -53,50 +45,17 @@ const Label = styled.span(
5345
`,
5446
);
5547

56-
const InfoIcon = styled(InfoCircleOutlined)(
48+
const DocsLink = styled.a(
5749
({ theme }) => `
58-
color: ${theme.colorTextSecondary};
50+
color: ${theme.colorPrimary};
51+
font-size: 12px;
52+
font-weight: 500;
53+
text-decoration: none;
5954
cursor: pointer;
60-
font-size: 14px;
6155
6256
&:hover {
63-
color: ${theme.colorPrimary};
64-
}
65-
`,
66-
);
67-
68-
const PopoverContentWrapper = styled.div`
69-
width: 380px;
70-
max-height: 450px;
71-
overflow: hidden;
72-
display: flex;
73-
flex-direction: column;
74-
`;
75-
76-
const PopoverHeader = styled.div`
77-
display: flex;
78-
justify-content: space-between;
79-
align-items: center;
80-
margin-bottom: 12px;
81-
`;
82-
83-
const TemplateCode = styled.pre(
84-
({ theme }) => `
85-
font-family: ${theme.fontFamilyCode};
86-
font-size: 11px;
87-
background: ${theme.colorBgContainer};
88-
border: 1px solid ${theme.colorBorder};
89-
border-radius: 4px;
90-
padding: 12px;
91-
margin: 0;
92-
overflow: auto;
93-
white-space: pre;
94-
color: ${theme.colorText};
95-
max-height: 380px;
96-
97-
.optional-comment {
98-
color: ${theme.colorTextSecondary};
99-
font-style: italic;
57+
text-decoration: underline;
58+
color: ${theme.colorPrimaryHover};
10059
}
10160
`,
10261
);
@@ -121,108 +80,8 @@ const EditorWrapper = styled.div`
12180
}
12281
`;
12382

124-
// Template JSON with optional fields marked
125-
const templateJson = `{
126-
"globalColoring": {
127-
"fillColor": [40, 147, 179, 255],
128-
"strokeColor": [0, 0, 0, 255],
129-
"strokeWidth": 2,
130-
"pointType": "circle", // optional
131-
"lineStyle": "solid",
132-
"fillPattern": "solid"
133-
},
134-
"pointSize": 6, // static size (number), OR use the object form below:
135-
// "pointSize": { // dynamic — scales point size by a data column value
136-
// "valueColumn": "your_numeric_column",
137-
// "startSize": 4, // pixel size at lowerBound
138-
// "endSize": 30, // pixel size at upperBound
139-
// "lowerBound": null, // optional — defaults to data min
140-
// "upperBound": null // optional — defaults to data max
141-
// },
142-
"colorByCategory": {
143-
"dimension": "category_field",
144-
"categoricalColors": [
145-
{
146-
"example_category_1": {
147-
"fillColor": [0, 0, 255, 255],
148-
"legend_entry_name": "category_1"
149-
}
150-
},
151-
{
152-
"example_category_2": {
153-
"fillColor": [255, 0, 0, 255],
154-
"legend_entry_name": "category_2"
155-
}
156-
}
157-
],
158-
"defaultLegendName": ["Other", "Unknown"]
159-
},
160-
"colorByValue": {
161-
"valueColumn": "column_metrics_will_be_applied_to",
162-
"upperBound": null, // optional
163-
"lowerBound": null, // optional
164-
"startColor": [0, 255, 0, 255],
165-
"endColor": [255, 0, 0, 255],
166-
"breakpoints": []
167-
},
168-
"textOverlayStyle": {
169-
"fontFamily": "Arial, sans-serif", // Web Safe Fonts: Arial, Verdana, Georgia, Times New Roman, Courier New
170-
"fontSize": 14,
171-
"bold": false,
172-
"offset": [0, 0] // pixel offset (x, y) from point
173-
},
174-
"legend": {
175-
"name": "human_readable_legend_chart_title",
176-
"title": "title_for_legend_section"
177-
}
178-
}`;
179-
180-
// Clean template (without comments) for copying
181-
const cleanTemplateJson = JSON.stringify(
182-
{
183-
globalColoring: {
184-
fillColor: [40, 147, 179, 255],
185-
strokeColor: [0, 0, 0, 255],
186-
strokeWidth: 2,
187-
pointType: 'circle',
188-
lineStyle: 'solid',
189-
fillPattern: 'solid',
190-
},
191-
pointSize: 6,
192-
colorByCategory: {
193-
dimension: 'category_field',
194-
categoricalColors: [
195-
{
196-
example_category_1: {
197-
fillColor: [0, 0, 255, 255],
198-
legend_entry_name: 'category_1',
199-
},
200-
},
201-
{
202-
example_category_2: {
203-
fillColor: [255, 0, 0, 255],
204-
legend_entry_name: 'category_2',
205-
},
206-
},
207-
],
208-
defaultLegendName: ['Other', 'Unknown'],
209-
},
210-
colorByValue: {
211-
valueColumn: 'column_metrics_will_be_applied_to',
212-
upperBound: null,
213-
lowerBound: null,
214-
startColor: [0, 255, 0, 255],
215-
endColor: [255, 0, 0, 255],
216-
breakpoints: [],
217-
},
218-
legend: {
219-
name: 'human_readable_legend_chart_title',
220-
title: 'title_for_legend_section',
221-
},
222-
},
223-
null,
224-
2,
225-
);
83+
const JSON_CONFIG_SPEC_URL =
84+
'https://github.com/raft-tech/GeoSet/wiki/JSON-Config-Spec';
22685

22786
type JsonEditorControlProps = {
22887
label?: string;
@@ -256,8 +115,6 @@ export default function JsonEditorControl({
256115
if (defaultValue != null) return defaultValue;
257116
return '';
258117
});
259-
const [copied, setCopied] = useState(false);
260-
261118
useEffect(() => {
262119
if (value != null && value !== '' && value !== localValue) {
263120
setLocalValue(normalizeJson(value));
@@ -277,66 +134,19 @@ export default function JsonEditorControl({
277134
}
278135
};
279136

280-
const handleCopy = async () => {
281-
try {
282-
await navigator.clipboard.writeText(cleanTemplateJson);
283-
setCopied(true);
284-
setTimeout(() => setCopied(false), 2000);
285-
} catch (err) {
286-
// Fallback for older browsers
287-
const textArea = document.createElement('textarea');
288-
textArea.value = cleanTemplateJson;
289-
document.body.appendChild(textArea);
290-
textArea.select();
291-
document.execCommand('copy');
292-
document.body.removeChild(textArea);
293-
setCopied(true);
294-
setTimeout(() => setCopied(false), 2000);
295-
}
296-
};
297-
298-
// Highlight optional comments in the template
299-
const highlightTemplate = (code: string) => {
300-
const highlighted = Prism.highlight(code, Prism.languages.json, 'json');
301-
return highlighted.replace(
302-
/(\/\/ optional)/g,
303-
'<span class="optional-comment">$1</span>',
304-
);
305-
};
306-
307-
const popoverContent = (
308-
<PopoverContentWrapper>
309-
<PopoverHeader>
310-
<Text strong>{t('Example Configuration')}</Text>
311-
<Button
312-
size="small"
313-
icon={copied ? <CheckOutlined /> : <CopyOutlined />}
314-
onClick={handleCopy}
315-
>
316-
{copied ? t('Copied!') : t('Copy')}
317-
</Button>
318-
</PopoverHeader>
319-
<TemplateCode
320-
dangerouslySetInnerHTML={{
321-
__html: highlightTemplate(templateJson),
322-
}}
323-
/>
324-
</PopoverContentWrapper>
325-
);
326-
327137
return (
328138
<Container>
329139
{label && (
330140
<LabelRow>
331141
<Label id={`${label}-label`}>{label}</Label>
332-
<Popover
333-
content={popoverContent}
334-
trigger="click"
335-
placement="rightTop"
336-
overlayStyle={{ maxWidth: 420 }}
142+
<DocsLink
143+
href={JSON_CONFIG_SPEC_URL}
144+
target="_blank"
145+
rel="noopener noreferrer"
146+
aria-label={t('View JSON Config Spec wiki')}
337147
>
338-
<InfoIcon title={t('View template configuration')} />
339-
</Popover>
148+
{t('[see docs]')}
149+
</DocsLink>
340150
</LabelRow>
341151
)}
342152
<EditorWrapper onBlur={handleBlur}>

0 commit comments

Comments
 (0)