Compare commits

..

1 Commits

Author SHA1 Message Date
Vitor Avila
a1851ff3b2 fix: Check python deps CI job 2026-04-30 17:20:14 -03:00
19 changed files with 90 additions and 652 deletions

View File

@@ -1,122 +0,0 @@
---
title: Glossary
hide_title: true
sidebar_position: 10
---
import { getAllGlossaryTopics } from '../../superset-frontend/packages/superset-ui-core/src/glossary';
import { Table, ConfigProvider, theme } from 'antd';
import { useColorMode } from '@docusaurus/theme-common';
import { useCallback, useEffect, useRef } from 'react';
export const GlossaryStructure = [
{
title: 'Term',
dataIndex: 'title',
key: 'title',
width: 200,
},
{
title: 'Short Description',
dataIndex: 'short',
key: 'short',
},
];
export const GlossaryContent = () => {
const { colorMode } = useColorMode();
const isDark = colorMode === 'dark';
const tableRefs = useRef({});
const scrollToRow = useCallback((topic, rowKey) => {
const topicId = encodeURIComponent(topic);
const encRowKey = encodeURIComponent(rowKey);
const row = tableRefs.current[topicId]?.[encRowKey];
if (row) {
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
row.classList.add('table-row-highlight');
setTimeout(() => row.classList.remove('table-row-highlight'), 2000);
}
}, []);
useEffect(() => {
let hash = '';
try {
hash = decodeURIComponent(window.location.hash.slice(1));
} catch (e) {
// Malformed percent-encoding in the URL hash — silently skip the
// scroll-to-row behavior rather than letting the page render fail.
return;
}
if (!hash) return;
const [topic, term] = hash.split('__');
if (topic && term) scrollToRow(topic, hash);
}, [scrollToRow]);
return (
<div>
<ConfigProvider
theme={{
algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm,
}}
>
{getAllGlossaryTopics().map((topic) => {
const topicName = topic.getName();
const topicFragment = encodeURIComponent(topicName);
const terms = topic.getAllTerms();
return (
<div key={topicName} id={topicFragment}>
<h3>{topic.getDisplayName()}</h3>
<Table
dataSource={terms
.map((term) => {
const key = term.getTitle()
? encodeURIComponent(`${topicName}__${term.getTitle()}`)
: undefined;
return key
? {
title: term.getDisplayTitle(),
short: term.getShort(),
key,
}
: null;
})
.filter(Boolean)}
columns={GlossaryStructure}
rowKey="key"
pagination={false}
showHeader
bordered
onRow={(record) => {
if (!record?.key) return {};
const topicId = topicFragment;
return {
ref: (node) => {
if (!tableRefs.current[topicId]) tableRefs.current[topicId] = {};
if (node) {
tableRefs.current[topicId][record.key] = node;
} else {
// cleanup stale reference when row unmounts
delete tableRefs.current[topicId][record.key];
if (Object.keys(tableRefs.current[topicId]).length === 0) {
delete tableRefs.current[topicId];
}
}
},
};
}}
/>
</div>
);
})}
</ConfigProvider>
</div>
);
};
## Glossary
<GlossaryContent />

View File

@@ -60,11 +60,6 @@ const sidebars = {
},
],
},
{
type: 'doc',
label: 'Glossary',
id: 'glossary'
},
{
type: 'doc',
label: 'FAQ',

View File

@@ -707,7 +707,7 @@ protobuf==4.25.8
# proto-plus
psutil==6.1.0
# via apache-superset
psycopg2-binary==2.9.9
psycopg2-binary==2.9.12
# via apache-superset
py-key-value-aio==0.4.4
# via fastmcp

View File

@@ -23,10 +23,6 @@ import { ControlSubSectionHeader } from '../components/ControlSubSectionHeader';
import { ControlPanelSectionConfig } from '../types';
import { formatSelectOptions, displayTimeRelatedControls } from '../utils';
import { glossary } from '@superset-ui/core';
const TIME_SHIFT_DESCRIPTION = glossary.Advanced_Analytics.Time_Shift.encode();
export const advancedAnalyticsControls: ControlPanelSectionConfig = {
label: t('Advanced analytics'),
tabOverride: 'data',
@@ -127,7 +123,12 @@ export const advancedAnalyticsControls: ControlPanelSectionConfig = {
['156 weeks ago', t('156 weeks ago')],
['3 years ago', t('3 years ago')],
],
description: TIME_SHIFT_DESCRIPTION,
description: t(
'Overlay one or more timeseries from a ' +
'relative time period. Expects relative time deltas ' +
'in natural language (example: 24 hours, 7 days, ' +
'52 weeks, 365 days). Free text is supported.',
),
},
},
],

View File

@@ -25,10 +25,6 @@ import {
ControlState,
} from '../types';
import { INVALID_DATE } from '..';
import { glossary } from '@superset-ui/core';
// Glossary terms used for tooltips
const TIME_SHIFT_DESCRIPTION = glossary.Advanced_Analytics.Time_Shift.encode();
const fullChoices = [
['1 day ago', t('1 day ago')],
@@ -86,7 +82,16 @@ export const timeComparisonControls: ({
placeholder: t('Select or type a custom value...'),
label: t('Time shift'),
choices: showFullChoices ? fullChoices : reducedChoices,
description: TIME_SHIFT_DESCRIPTION,
description: t(
'Overlay results from a relative time period. ' +
'Expects relative time deltas ' +
'in natural language (example: 24 hours, 7 days, ' +
'52 weeks, 365 days). Free text is supported. ' +
'Use "Inherit range from time filters" ' +
'to shift the comparison time range ' +
'by the same length as your time range ' +
'and use "Custom" to set a custom comparison range.',
),
},
},
],

View File

@@ -39,13 +39,6 @@ import {
xAxisMixin,
} from '..';
import { glossary } from '@superset-ui/core';
// Glossary terms used for tooltips
const DIMENSION_DESCRIPTION = glossary.Query.Dimension.encode();
const METRIC_DESCRIPTION = glossary.Query.Metric.encode();
const SORT_DESCRIPTION = glossary.Query.Sort.encode();
type Control = {
savedMetrics?: Metric[] | null;
default?: unknown;
@@ -85,7 +78,11 @@ export const dndGroupByControl: SharedControlConfig<
clearable: true,
default: [],
includeTime: false,
description: DIMENSION_DESCRIPTION,
description: t(
'Dimensions contain qualitative values such as names, dates, or geographical data. ' +
'Use dimensions to categorize, segment, and reveal the details in your data. ' +
'Dimensions affect the level of detail in the view.',
),
optionRenderer: (c: ColumnMeta) => <ColumnOption showType column={c} />,
valueRenderer: (c: ColumnMeta) => <ColumnOption column={c} />,
valueKey: 'column_name',
@@ -183,7 +180,11 @@ export const dndAdhocMetricsControl: SharedControlConfig<
datasource,
datasourceType: datasource?.type,
}),
description: METRIC_DESCRIPTION,
description: t(
'Select one or many metrics to display. ' +
'You can use an aggregation function on a column ' +
'or write custom SQL to create a metric.',
),
};
export const dndAdhocMetricControl: typeof dndAdhocMetricsControl = {
@@ -223,7 +224,11 @@ export const dndSortByControl: SharedControlConfig<
type: 'DndMetricSelect',
label: t('Sort query by'),
default: null,
description: SORT_DESCRIPTION,
description: t(
'Orders the query result that generates the source data for this chart. ' +
'If a series or row limit is reached, this determines what data are truncated. ' +
'If undefined, defaults to the first metric (where appropriate).',
),
mapStateToProps: ({ datasource }) => ({
columns: datasource?.columns || [],
savedMetrics: defineSavedMetrics(datasource),

View File

@@ -86,10 +86,6 @@ import {
dndTooltipMetricsControl,
} from './dndControls';
import { matrixifyControls } from './matrixifyControls';
import { glossary } from '@superset-ui/core';
const SERIES_DESCRIPTION = glossary.Query.Series.encode();
const ROW_LIMIT_DESCRIPTION = glossary.Query.Row_Limit.encode();
const categoricalSchemeRegistry = getCategoricalSchemeRegistry();
const sequentialSchemeRegistry = getSequentialSchemeRegistry();
@@ -239,7 +235,9 @@ const row_limit: SharedControlConfig<'SelectControl'> = {
],
default: 10000,
choices: formatSelectOptions(ROW_LIMIT_OPTIONS),
description: ROW_LIMIT_DESCRIPTION,
description: t(
'Limits the number of the rows that are computed in the query that is the source of the data used for this chart.',
),
};
const order_desc: SharedControlConfig<'CheckboxControl'> = {
@@ -264,7 +262,12 @@ const limit: SharedControlConfig<'SelectControl'> = {
validators: [legacyValidateInteger],
choices: formatSelectOptions(SERIES_LIMITS),
clearable: true,
description: SERIES_DESCRIPTION,
description: t(
'Limits the number of series that get displayed. A joined subquery (or an extra phase ' +
'where subqueries are not supported) is applied to limit the number of series that get ' +
'fetched and rendered. This feature is useful when grouping by high cardinality ' +
'column(s) though does increase the query complexity and cost.',
),
};
const series_limit: SharedControlConfig<'SelectControl'> = {
@@ -274,7 +277,12 @@ const series_limit: SharedControlConfig<'SelectControl'> = {
placeholder: t('None'),
validators: [legacyValidateInteger],
choices: formatSelectOptions(SERIES_LIMITS),
description: SERIES_DESCRIPTION,
description: t(
'Limits the number of series that get displayed. A joined subquery (or an extra phase ' +
'where subqueries are not supported) is applied to limit the number of series that get ' +
'fetched and rendered. This feature is useful when grouping by high cardinality ' +
'column(s) though does increase the query complexity and cost.',
),
};
const group_others_when_limit_reached: SharedControlConfig<'CheckboxControl'> =

View File

@@ -16,70 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
import type { CSSProperties } from 'react';
import { Tooltip as AntdTooltip } from 'antd';
import type { TooltipProps, TooltipPlacement } from './types';
import { resolveGlossaryString } from '@superset-ui/core';
const TOOLTIP_SEPARATOR_STYLE: CSSProperties = {
margin: '8px 0',
border: 'none',
borderTop: '1px solid rgba(255, 255, 255, 0.2)',
};
export const Tooltip = ({
overlayStyle,
title,
children,
...props
}: TooltipProps) => {
if (typeof title !== 'string') {
return (
<AntdTooltip
title={title}
styles={{
body: { overflow: 'hidden', textOverflow: 'ellipsis' },
root: overlayStyle ?? {},
}}
{...props}
>
{children}
</AntdTooltip>
);
}
const [glossaryUrl, description] = resolveGlossaryString(title);
const wrappedChildren = glossaryUrl ? (
<a href={glossaryUrl} target="_blank" rel="noopener noreferrer">
{children}
</a>
) : (
children
);
const wrappedDescription = glossaryUrl ? (
<>
{description}
<hr style={TOOLTIP_SEPARATOR_STYLE} />
<em>Click to Learn More</em>
</>
) : (
description
);
return (
<AntdTooltip
title={wrappedDescription}
styles={{
body: { overflow: 'hidden', textOverflow: 'ellipsis' },
root: overlayStyle ?? {},
}}
{...props}
>
{wrappedChildren}
</AntdTooltip>
);
};
export const Tooltip = ({ overlayStyle, ...props }: TooltipProps) => (
<AntdTooltip
styles={{
body: { overflow: 'hidden', textOverflow: 'ellipsis' },
root: overlayStyle ?? {},
}}
{...props}
/>
);
export type { TooltipProps, TooltipPlacement };

View File

@@ -1,121 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Glossary definition containing terms organized by topic.
*
* ## How to add new glossary entries:
*
* 1. Add a new topic (if needed) or use an existing one
* 2. Add a term under the topic with a key (term name) and value object containing:
* - short: A brief description (displayed in tooltips)
* - extended (optional): An extended description (displayed in documentation)
*
* ## Example:
* export const glossaryDefinition: GlossaryDefinition = {
* Query: {
* Row_Limit: {
* short: noTranslate('Limits the number of rows...'),
* extended: noTranslate('Additional details...'), // optional
* },
* },
* };
*
* ## Formatting Notes:
* - Term names with underscores (e.g., `Row_Limit`) will be displayed with spaces
* (e.g., "Row Limit") when rendered in the UI and documentation
*/
export const glossaryDefinition: GlossaryDefinition = {
Query: {
Dimension: {
short: noTranslate(
'Dimensions contain qualitative values such as names, dates, or geographical data. ' +
'Use dimensions to categorize, segment, and reveal the details in your data. ' +
'Dimensions affect the level of detail in the view.',
),
},
Metric: {
short: noTranslate(
'Select one or many metrics to display. ' +
'You can use an aggregation function on a column or write custom SQL to create a metric.',
),
},
Series: {
short: noTranslate(
'Limits the number of series that get displayed. ' +
'A joined subquery (or an extra phase where subqueries are not supported) is applied ' +
'to limit the number of series that get fetched and rendered. ' +
'This feature is useful when grouping by high cardinality column(s) ' +
'though does increase the query complexity and cost.',
),
},
Row_Limit: {
short: noTranslate(
'Limits the number of rows that get displayed. ' +
'This feature is useful when grouping by high cardinality column(s) ' +
'though does increase the query complexity and cost.',
),
},
Sort: {
short: noTranslate(
'Orders the query result that generates the source data for this chart. ' +
'If a series or row limit is reached, this determines what data are truncated. ' +
'If undefined, defaults to the first metric (where appropriate).',
),
},
},
Advanced_Analytics: {
Time_Shift: {
short: noTranslate(
'Overlay results from a relative time period. ' +
'Expects relative time deltas in natural language (example: 24 hours, 7 days, ' +
'52 weeks, 365 days). Free text is supported. ' +
'Use "Inherit range from time filters" to shift the comparison time range ' +
'by the same length as your time range and use "Custom" to set a custom comparison range.',
),
},
},
};
/**
* Identity passthrough used in environments (such as the docs site) that do
* not have an i18n runtime. Translation of glossary strings is performed at
* resolution time by callers in app contexts that do have i18n available.
*
* Named `noTranslate` (rather than `t`) so it does not visually shadow the
* imported i18n `t` used elsewhere in this package.
*/
function noTranslate(message: string): string {
return message;
}
/**
* The glossary definition is a nested object where the first level keys are topics,
* and the second level keys are term titles. This remains a static string-based
* structure, mainly for good IDE autocomplete.
*/
export type GlossaryStrings = {
short: string;
extended?: string;
};
export type GlossaryDefinition = Record<
string,
Record<string, GlossaryStrings>
>;

View File

@@ -1,154 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// Local type definition to avoid circular dependency with glossaryUtils
type Glossary = Record<string, Record<string, GlossaryTerm>>;
// Encoding format prefix for glossary strings
export const GLOSSARY_ENCODING_PREFIX = '[GLOSSARY]|';
export class GlossaryTerm {
/**
* The topic under which the term is categorized.
*/
private readonly topic: string;
/**
* The name of the term being defined.
*/
private readonly title: string;
/**
* A short description of the term. Displayed on the frontend as a tooltip.
*/
private readonly short: string;
/**
* An extended description of the term, shown alongside short on the documentation.
*/
private readonly extended?: string;
constructor(options: {
topic: string;
title: string;
short: string;
extended?: string;
}) {
this.topic = options.topic;
this.title = options.title;
this.short = options.short;
this.extended = options.extended;
}
getTopic(): string {
return this.topic;
}
getTitle(): string {
return this.title;
}
/**
* Returns a formatted display version of the title with underscores replaced by spaces.
*/
getDisplayTitle(): string {
return this.title.replace(/_/g, ' ');
}
/**
* Returns the short description, optionally transformed by a provided translation function.
*/
getShort(t?: (value: string) => string): string {
if (!t) {
return this.short;
}
return t(this.short);
}
getExtended(t?: (value: string) => string): string | undefined {
if (!t) {
return this.extended;
}
if (!this.extended) {
return undefined;
}
return t(this.extended);
}
/**
* Encodes the glossary term into a string format that can be resolved later.
* Format: [GLOSSARY]|topic|title
*/
encode(): string {
return `${GLOSSARY_ENCODING_PREFIX}${this.topic}|${this.title}`;
}
}
export class GlossaryTopic {
private readonly name: string;
private readonly terms: Map<string, GlossaryTerm>;
constructor(name: string, terms: GlossaryTerm[]) {
this.name = name;
this.terms = new Map(terms.map(term => [term.getTitle(), term]));
}
getName(): string {
return this.name;
}
/**
* Returns a formatted display version of the topic name with underscores replaced by spaces.
*/
getDisplayName(): string {
return this.name.replace(/_/g, ' ');
}
getTerm(title: string): GlossaryTerm | undefined {
return this.terms.get(title);
}
getAllTerms(): GlossaryTerm[] {
return Array.from(this.terms.values());
}
}
export class GlossaryMap {
private readonly topics: Map<string, GlossaryTopic>;
constructor(glossary: Glossary) {
const topics = new Map<string, GlossaryTopic>();
Object.entries(glossary).forEach(([topicName, termsByTitle]) => {
const topicTerms = Object.values(termsByTitle);
topics.set(topicName, new GlossaryTopic(topicName, topicTerms));
});
this.topics = topics;
}
getTopic(topicName: string): GlossaryTopic | undefined {
return this.topics.get(topicName);
}
getAllTopics(): GlossaryTopic[] {
return Array.from(this.topics.values());
}
}

View File

@@ -1,63 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import {
GlossaryMap,
GlossaryTerm,
type GlossaryTopic,
} from './glossaryModels';
import { glossaryDefinition } from './glossary';
/**
* The exported glossary object is a runtime structure where each entry is a GlossaryTerm instance, but the key
* structure mirrors `glossaryDefinition` so IDEs can autocomplete, yet callers can use methods like `getShort()`.
*/
export type Glossary = {
[Topic in keyof typeof glossaryDefinition]: {
[Title in keyof (typeof glossaryDefinition)[Topic]]: GlossaryTerm;
};
};
const glossary: Glossary = Object.fromEntries(
Object.entries(glossaryDefinition).map(([topic, termsByTitle]) => [
topic,
Object.fromEntries(
Object.entries(termsByTitle).map(([title, termStrings]) => [
title,
new GlossaryTerm({
topic,
title,
short: termStrings.short,
extended: termStrings.extended ?? '',
}),
]),
),
]),
) as Glossary;
const glossaryMap = new GlossaryMap(glossary);
export const getAllGlossaryTopics = (): GlossaryTopic[] =>
glossaryMap.getAllTopics();
export const getGlossaryTopic = (
topicName: string,
): GlossaryTopic | undefined => glossaryMap.getTopic(topicName);
export default glossary;

View File

@@ -1,26 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { GlossaryTerm, GlossaryTopic } from './glossaryModels';
export {
default as glossary,
getAllGlossaryTopics,
getGlossaryTopic,
} from './glossaryUtils';
export { resolveGlossaryString } from './tooltipUtils';

View File

@@ -1,50 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { getGlossaryTopic } from './glossaryUtils';
import { t } from '@superset-ui/core';
export const GLOSSARY_BASE_URL = 'https://superset.apache.org/docs';
// Pattern matches: [GLOSSARY]|topic|title
// Captures: topic and title for lookup in glossary
const GLOSSARY_ENCODING_PATTERN = /^\[GLOSSARY\]\|([^|]+)\|([^|]+)$/;
export const resolveGlossaryString = (
glossaryString: string,
): [string | undefined, string] => {
const encoded = glossaryString.trim();
const match = encoded.match(GLOSSARY_ENCODING_PATTERN);
if (!match) {
return [undefined, encoded];
}
const topic = match[1];
const title = match[2];
// Look up the term from the glossary to get the translated description
const glossaryTopic = getGlossaryTopic(topic);
const term = glossaryTopic?.getTerm(title);
const description = term ? term.getShort(t) : encoded;
const glossaryUrl = buildGlossaryUrl(topic, title);
return [glossaryUrl, description];
};
const buildGlossaryUrl = (topic: string, title: string): string =>
`${GLOSSARY_BASE_URL}/glossary#${encodeURIComponent(`${topic}__${title}`)}`;

View File

@@ -35,4 +35,3 @@ export * from './ui-overrides';
export * from './hooks';
export * from './currency-format';
export * from './time-comparison';
export * from './glossary';

View File

@@ -28,9 +28,6 @@ import {
getStandardizedControls,
} from '@superset-ui/chart-controls';
import OptionDescription from './OptionDescription';
import { glossary } from '@superset-ui/core';
const TIME_SHIFT_DESCRIPTION = glossary.Advanced_Analytics.Time_Shift.encode();
const config: ControlPanelConfig = {
controlPanelSections: [
@@ -324,7 +321,12 @@ const config: ControlPanelConfig = {
['156 weeks', t('156 weeks')],
['3 years', t('3 years')],
],
description: TIME_SHIFT_DESCRIPTION,
description: t(
'Overlay one or more timeseries from a ' +
'relative time period. Expects relative time deltas ' +
'in natural language (example: 24 hours, 7 days, ' +
'52 weeks, 365 days). Free text is supported.',
),
},
},
{

View File

@@ -26,9 +26,6 @@ import {
sections,
getStandardizedControls,
} from '@superset-ui/chart-controls';
import { glossary } from '@superset-ui/core';
const TIME_SHIFT_DESCRIPTION = glossary.Advanced_Analytics.Time_Shift.encode();
const config: ControlPanelConfig = {
controlPanelSections: [
@@ -207,7 +204,12 @@ const config: ControlPanelConfig = {
['156 weeks', t('156 weeks')],
['3 years', t('3 years')],
],
description: TIME_SHIFT_DESCRIPTION,
description: t(
'Overlay one or more timeseries from a ' +
'relative time period. Expects relative time deltas ' +
'in natural language (example: 24 hours, 7 days, ' +
'52 weeks, 365 days). Free text is supported.',
),
},
},
{

View File

@@ -28,10 +28,6 @@ import {
D3_FORMAT_OPTIONS,
} from '@superset-ui/chart-controls';
import { glossary } from '@superset-ui/core';
const TIME_SHIFT_DESCRIPTION = glossary.Advanced_Analytics.Time_Shift.encode();
/*
Plugins in question:
@@ -476,7 +472,12 @@ export const timeSeriesSection: ControlPanelSectionConfig[] = [
['156 weeks', t('156 weeks')],
['3 years', t('3 years')],
],
description: TIME_SHIFT_DESCRIPTION,
description: t(
'Overlay one or more timeseries from a ' +
'relative time period. Expects relative time deltas ' +
'in natural language (example: 24 hours, 7 days, ' +
'52 weeks, 365 days). Free text is supported.',
),
},
},
],

View File

@@ -21,6 +21,7 @@ import { t } from '@apache-superset/core/translation';
import { css, useTheme, SupersetTheme } from '@apache-superset/core/theme';
import { FormLabel, InfoTooltip, Tooltip } from '@superset-ui/core/components';
import { Icons } from '@superset-ui/core/components/Icons';
type ValidationError = string;
export type ControlHeaderProps = {
@@ -92,8 +93,15 @@ const ControlHeader: FC<ControlHeaderProps> = ({
>
{description && (
<span>
<Tooltip title={description}>
<Icons.InfoCircleOutlined css={iconStyles} />
<Tooltip
id="description-tooltip"
title={description}
placement="top"
>
<Icons.InfoCircleOutlined
css={iconStyles}
onClick={tooltipOnClick}
/>
</Tooltip>{' '}
</span>
)}

View File

@@ -22,10 +22,6 @@ import {
ControlSubSectionHeader,
} from '@superset-ui/chart-controls';
import { glossary } from '@superset-ui/core';
const TIME_SHIFT_DESCRIPTION = glossary.Advanced_Analytics.Time_Shift.encode();
export const datasourceAndVizType: ControlPanelSectionConfig = {
controlSetRows: [
['datasource'],
@@ -207,7 +203,12 @@ export const NVD3TimeSeries: ControlPanelSectionConfig[] = [
['156 weeks', t('156 weeks')],
['3 years', t('3 years')],
],
description: TIME_SHIFT_DESCRIPTION,
description: t(
'Overlay one or more timeseries from a ' +
'relative time period. Expects relative time deltas ' +
'in natural language (example: 24 hours, 7 days, ' +
'52 weeks, 365 days). Free text is supported.',
),
},
},
{