feat: redesign labels (#31575)
This commit is contained in:
parent
8a2aada58d
commit
740fbf72d7
|
|
@ -25,23 +25,96 @@ export default {
|
||||||
|
|
||||||
export const ThemeColors = () => {
|
export const ThemeColors = () => {
|
||||||
const { colors } = supersetTheme;
|
const { colors } = supersetTheme;
|
||||||
return Object.keys(colors).map(collection => (
|
|
||||||
|
// Define tones to be displayed in columns
|
||||||
|
const tones = [
|
||||||
|
'dark2',
|
||||||
|
'dark1',
|
||||||
|
'base',
|
||||||
|
'light1',
|
||||||
|
'light2',
|
||||||
|
'light3',
|
||||||
|
'light4',
|
||||||
|
'light5',
|
||||||
|
];
|
||||||
|
const colorTypes = [
|
||||||
|
'primary',
|
||||||
|
'secondary',
|
||||||
|
'grayscale',
|
||||||
|
'error',
|
||||||
|
'warning',
|
||||||
|
'alert',
|
||||||
|
'success',
|
||||||
|
'info',
|
||||||
|
];
|
||||||
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>{collection}</h2>
|
<h1>Theme Colors</h1>
|
||||||
<table style={{ width: '300px' }}>
|
<table
|
||||||
{Object.keys(colors[collection]).map(k => {
|
style={{ borderCollapse: 'collapse', width: '100%', textAlign: 'left' }}
|
||||||
const hex = colors[collection][k];
|
>
|
||||||
return (
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{k}</td>
|
<th style={{ border: '1px solid #ddd', padding: '8px' }}>
|
||||||
<td>
|
Category
|
||||||
<code>{hex}</code>
|
</th>
|
||||||
|
{tones.map(tone => (
|
||||||
|
<th
|
||||||
|
key={tone}
|
||||||
|
style={{ border: '1px solid #ddd', padding: '8px' }}
|
||||||
|
>
|
||||||
|
{tone}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{colorTypes.map(category => (
|
||||||
|
<tr key={category}>
|
||||||
|
<td style={{ border: '1px solid #ddd', padding: '8px' }}>
|
||||||
|
<strong>{category}</strong>
|
||||||
</td>
|
</td>
|
||||||
<td style={{ width: '150px', backgroundColor: hex }} />
|
{tones.map(tone => {
|
||||||
|
const color = colors[category][tone];
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
key={tone}
|
||||||
|
style={{
|
||||||
|
border: '1px solid #ddd',
|
||||||
|
padding: '8px',
|
||||||
|
backgroundColor: color || '#fff',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{color ? <code>{color}</code> : '-'}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
))}
|
||||||
})}
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<h3>
|
||||||
|
text.label: <code>{colors.text.label}</code>
|
||||||
|
</h3>
|
||||||
|
<div style={{ color: `#${colors.text.label}` }}>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
||||||
|
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||||
|
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
||||||
|
commodo consequat.
|
||||||
|
</div>
|
||||||
|
<h3>
|
||||||
|
text.help: <code>{colors.text.help}</code>
|
||||||
|
</h3>
|
||||||
|
<div style={{ color: `#${colors.text.help}` }}>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
||||||
|
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||||
|
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
||||||
|
commodo consequat.
|
||||||
|
</div>
|
||||||
|
<h3>The supersetTheme object</h3>
|
||||||
|
<code>
|
||||||
|
<pre>{JSON.stringify(supersetTheme, null, 2)}</pre>
|
||||||
|
</code>
|
||||||
</div>
|
</div>
|
||||||
));
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,11 @@
|
||||||
import { useCallback, useEffect, useMemo, useState, FC } from 'react';
|
import { useCallback, useEffect, useMemo, useState, FC } from 'react';
|
||||||
|
|
||||||
import { isEqual, isEmpty } from 'lodash';
|
import { isEqual, isEmpty } from 'lodash';
|
||||||
import { QueryFormData, styled, t } from '@superset-ui/core';
|
import { QueryFormData, t } from '@superset-ui/core';
|
||||||
import { sanitizeFormData } from 'src/explore/exploreUtils/formData';
|
import { sanitizeFormData } from 'src/explore/exploreUtils/formData';
|
||||||
import getControlsForVizType from 'src/utils/getControlsForVizType';
|
import getControlsForVizType from 'src/utils/getControlsForVizType';
|
||||||
|
import Label from 'src/components/Label';
|
||||||
|
import Icons from 'src/components/Icons';
|
||||||
import { safeStringify } from 'src/utils/safeStringify';
|
import { safeStringify } from 'src/utils/safeStringify';
|
||||||
import { Tooltip } from 'src/components/Tooltip';
|
import { Tooltip } from 'src/components/Tooltip';
|
||||||
import ModalTrigger from '../ModalTrigger';
|
import ModalTrigger from '../ModalTrigger';
|
||||||
|
|
@ -68,18 +70,6 @@ export type RowType = {
|
||||||
control: string;
|
control: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledLabel = styled.span`
|
|
||||||
${({ theme }) => `
|
|
||||||
font-size: ${theme.typography.sizes.s}px;
|
|
||||||
color: ${theme.colors.grayscale.dark1};
|
|
||||||
background-color: ${theme.colors.alert.base};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: ${theme.colors.alert.dark1};
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const alterForComparison = (
|
export const alterForComparison = (
|
||||||
value?: string | null | [],
|
value?: string | null | [],
|
||||||
): string | null => {
|
): string | null => {
|
||||||
|
|
@ -228,7 +218,14 @@ const AlteredSliceTag: FC<AlteredSliceTagProps> = props => {
|
||||||
const triggerNode = useMemo(
|
const triggerNode = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<Tooltip id="difference-tooltip" title={t('Click to see difference')}>
|
<Tooltip id="difference-tooltip" title={t('Click to see difference')}>
|
||||||
<StyledLabel className="label">{t('Altered')}</StyledLabel>
|
<Label
|
||||||
|
icon={<Icons.Warning iconSize="m" />}
|
||||||
|
className="label"
|
||||||
|
type="alert"
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
{t('Altered')}
|
||||||
|
</Label>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
),
|
),
|
||||||
[],
|
[],
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ const AntdIconComponent = ({
|
||||||
|
|
||||||
export const StyledIcon = styled(AntdIconComponent)<IconType>`
|
export const StyledIcon = styled(AntdIconComponent)<IconType>`
|
||||||
${({ iconColor }) => iconColor && `color: ${iconColor};`};
|
${({ iconColor }) => iconColor && `color: ${iconColor};`};
|
||||||
|
span {
|
||||||
|
// Fixing alignement on some of the icons
|
||||||
|
line-height: 0px;
|
||||||
|
}
|
||||||
font-size: ${({ iconSize, theme }) =>
|
font-size: ${({ iconSize, theme }) =>
|
||||||
iconSize
|
iconSize
|
||||||
? `${theme.typography.sizes[iconSize] || theme.typography.sizes.m}px`
|
? `${theme.typography.sizes[iconSize] || theme.typography.sizes.m}px`
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,17 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import Label, { Type } from './index';
|
import { Meta, StoryFn } from '@storybook/react';
|
||||||
|
import Label, { Type, DatasetTypeLabel, PublishedLabel } from './index';
|
||||||
|
|
||||||
|
// Define the default export with Storybook configuration
|
||||||
export default {
|
export default {
|
||||||
title: 'Label',
|
title: 'Label',
|
||||||
component: Label,
|
component: Label,
|
||||||
excludeStories: 'options',
|
excludeStories: ['options'],
|
||||||
};
|
} as Meta<typeof Label>;
|
||||||
|
|
||||||
|
// Explicitly type the options array as an array of `Type`
|
||||||
export const options: Type[] = [
|
export const options: Type[] = [
|
||||||
'default',
|
'default',
|
||||||
'alert',
|
'alert',
|
||||||
|
|
@ -36,39 +39,60 @@ export const options: Type[] = [
|
||||||
'secondary',
|
'secondary',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const LabelGallery = () => (
|
// Define the props for the `LabelGallery` component
|
||||||
<>
|
interface LabelGalleryProps {
|
||||||
<h4>Non-interactive</h4>
|
hasOnClick?: boolean;
|
||||||
{Object.values(options).map((opt: Type) => (
|
monospace?: boolean;
|
||||||
<Label key={opt} type={opt}>
|
}
|
||||||
{`style: "${opt}"`}
|
|
||||||
</Label>
|
// Use the `StoryFn` type for LabelGallery
|
||||||
))}
|
export const LabelGallery: StoryFn<LabelGalleryProps> = (
|
||||||
<br />
|
props: LabelGalleryProps,
|
||||||
<h4>Interactive</h4>
|
) => {
|
||||||
{Object.values(options).map((opt: Type) => (
|
const onClick = props.hasOnClick ? action('clicked') : undefined;
|
||||||
<Label key={opt} type={opt} onClick={action('clicked')}>
|
|
||||||
{`style: "${opt}"`}
|
|
||||||
</Label>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const InteractiveLabel = (args: any) => {
|
|
||||||
const { hasOnClick, label, monospace, ...rest } = args;
|
|
||||||
return (
|
return (
|
||||||
<Label
|
<>
|
||||||
onClick={hasOnClick ? action('clicked') : undefined}
|
<h4>Non-interactive</h4>
|
||||||
monospace={monospace}
|
{options.map((opt: Type) => (
|
||||||
{...rest}
|
<Label key={opt} type={opt}>
|
||||||
>
|
{`style: "${opt}"`}
|
||||||
{label}
|
</Label>
|
||||||
</Label>
|
))}
|
||||||
|
<br />
|
||||||
|
<h4>Interactive</h4>
|
||||||
|
{options.map((opt: Type) => (
|
||||||
|
<Label key={opt} type={opt} {...props} onClick={onClick}>
|
||||||
|
{`style: "${opt}"`}
|
||||||
|
</Label>
|
||||||
|
))}
|
||||||
|
<h4>Reusable Labels</h4>
|
||||||
|
<h5>DatasetType</h5>
|
||||||
|
<div>
|
||||||
|
<DatasetTypeLabel datasetType="physical" />
|
||||||
|
<DatasetTypeLabel datasetType="virtual" />
|
||||||
|
</div>
|
||||||
|
<h5>PublishedLabel</h5>
|
||||||
|
<PublishedLabel isPublished />
|
||||||
|
<PublishedLabel isPublished={false} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
InteractiveLabel.args = {
|
// Define default arguments for Storybook
|
||||||
|
LabelGallery.args = {
|
||||||
hasOnClick: true,
|
hasOnClick: true,
|
||||||
label: 'Example',
|
monospace: false,
|
||||||
monospace: true,
|
};
|
||||||
|
|
||||||
|
// Define argument types for Storybook controls
|
||||||
|
LabelGallery.argTypes = {
|
||||||
|
monospace: {
|
||||||
|
name: 'monospace',
|
||||||
|
control: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
hasOnClick: {
|
||||||
|
name: 'hasOnClick',
|
||||||
|
control: { type: 'boolean' },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ test('works with an onClick handler', () => {
|
||||||
// test stories from the storybook!
|
// test stories from the storybook!
|
||||||
test('renders all the storybook gallery variants', () => {
|
test('renders all the storybook gallery variants', () => {
|
||||||
const { container } = render(<LabelGallery />);
|
const { container } = render(<LabelGallery />);
|
||||||
|
const nonInteractiveLabelCount = 4;
|
||||||
|
const renderedLabelCount = options.length * 2 + nonInteractiveLabelCount;
|
||||||
expect(container.querySelectorAll('.ant-tag')).toHaveLength(
|
expect(container.querySelectorAll('.ant-tag')).toHaveLength(
|
||||||
options.length * 2,
|
renderedLabelCount,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ import {
|
||||||
|
|
||||||
import { Tag } from 'src/components';
|
import { Tag } from 'src/components';
|
||||||
import { useTheme } from '@superset-ui/core';
|
import { useTheme } from '@superset-ui/core';
|
||||||
|
import DatasetTypeLabel from 'src/components/Label/reusable/DatasetTypeLabel';
|
||||||
|
import PublishedLabel from 'src/components/Label/reusable/PublishedLabel';
|
||||||
|
|
||||||
export type OnClickHandler = MouseEventHandler<HTMLElement>;
|
export type OnClickHandler = MouseEventHandler<HTMLElement>;
|
||||||
|
|
||||||
|
|
@ -47,6 +49,7 @@ export interface LabelProps extends HTMLAttributes<HTMLSpanElement> {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
role?: string;
|
role?: string;
|
||||||
monospace?: boolean;
|
monospace?: boolean;
|
||||||
|
icon?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Label(props: LabelProps) {
|
export default function Label(props: LabelProps) {
|
||||||
|
|
@ -58,6 +61,7 @@ export default function Label(props: LabelProps) {
|
||||||
style,
|
style,
|
||||||
onClick,
|
onClick,
|
||||||
children,
|
children,
|
||||||
|
icon,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
|
|
@ -71,37 +75,44 @@ export default function Label(props: LabelProps) {
|
||||||
info,
|
info,
|
||||||
} = colors;
|
} = colors;
|
||||||
|
|
||||||
let backgroundColor = grayscale.light3;
|
let baseColor;
|
||||||
let backgroundColorHover = onClick ? primary.light2 : grayscale.light3;
|
if (type === 'primary') {
|
||||||
let borderColor = onClick ? grayscale.light2 : 'transparent';
|
baseColor = primary;
|
||||||
let borderColorHover = onClick ? primary.light1 : 'transparent';
|
} else if (type === 'secondary') {
|
||||||
let color = grayscale.dark1;
|
baseColor = secondary;
|
||||||
|
} else if (type === 'success') {
|
||||||
if (type !== 'default') {
|
baseColor = success;
|
||||||
color = grayscale.light4;
|
} else if (type === 'alert') {
|
||||||
|
baseColor = alert;
|
||||||
let baseColor;
|
} else if (type === 'warning') {
|
||||||
if (type === 'alert') {
|
baseColor = warning;
|
||||||
color = grayscale.dark1;
|
} else if (type === 'danger') {
|
||||||
baseColor = alert;
|
baseColor = error;
|
||||||
} else if (type === 'success') {
|
} else if (type === 'info') {
|
||||||
baseColor = success;
|
baseColor = info;
|
||||||
} else if (type === 'warning') {
|
} else {
|
||||||
baseColor = warning;
|
baseColor = grayscale;
|
||||||
} else if (type === 'danger') {
|
|
||||||
baseColor = error;
|
|
||||||
} else if (type === 'info') {
|
|
||||||
baseColor = info;
|
|
||||||
} else if (type === 'secondary') {
|
|
||||||
baseColor = secondary;
|
|
||||||
} else {
|
|
||||||
baseColor = primary;
|
|
||||||
}
|
|
||||||
backgroundColor = baseColor.base;
|
|
||||||
backgroundColorHover = onClick ? baseColor.dark1 : baseColor.base;
|
|
||||||
borderColor = onClick ? baseColor.dark1 : 'transparent';
|
|
||||||
borderColorHover = onClick ? baseColor.dark2 : 'transparent';
|
|
||||||
}
|
}
|
||||||
|
const color = baseColor.dark2;
|
||||||
|
let borderColor = baseColor.light1;
|
||||||
|
let backgroundColor = baseColor.light2;
|
||||||
|
|
||||||
|
// TODO - REMOVE IF BLOCK LOGIC WHEN shades are fixed to be aligned in terms of brightness
|
||||||
|
// currently shades for >=light2 are not aligned for primary, default and secondary
|
||||||
|
if (['default', 'primary', 'secondary'].includes(type)) {
|
||||||
|
// @ts-ignore
|
||||||
|
backgroundColor = baseColor.light4;
|
||||||
|
borderColor = baseColor.light2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backgroundColorHover = onClick ? baseColor.light1 : backgroundColor;
|
||||||
|
const borderColorHover = onClick ? baseColor.base : borderColor;
|
||||||
|
|
||||||
|
if (type === 'default') {
|
||||||
|
// Lighter for default
|
||||||
|
backgroundColor = grayscale.light3;
|
||||||
|
}
|
||||||
|
|
||||||
const css = {
|
const css = {
|
||||||
transition: `background-color ${transitionTiming}s`,
|
transition: `background-color ${transitionTiming}s`,
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
|
@ -109,11 +120,14 @@ export default function Label(props: LabelProps) {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
|
borderRadius: 8,
|
||||||
borderColor,
|
borderColor,
|
||||||
borderRadius: 21,
|
|
||||||
padding: '0.35em 0.8em',
|
padding: '0.35em 0.8em',
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
color,
|
color,
|
||||||
|
display: 'inline-flex',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
alignItems: 'center',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: backgroundColorHover,
|
backgroundColor: backgroundColorHover,
|
||||||
|
|
@ -124,12 +138,12 @@ export default function Label(props: LabelProps) {
|
||||||
if (monospace) {
|
if (monospace) {
|
||||||
css['font-family'] = theme.typography.families.monospace;
|
css['font-family'] = theme.typography.families.monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
role={onClick ? 'button' : undefined}
|
role={onClick ? 'button' : undefined}
|
||||||
style={style}
|
style={style}
|
||||||
|
icon={icon}
|
||||||
{...rest}
|
{...rest}
|
||||||
css={css}
|
css={css}
|
||||||
>
|
>
|
||||||
|
|
@ -137,3 +151,4 @@ export default function Label(props: LabelProps) {
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
export { DatasetTypeLabel, PublishedLabel };
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* 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 Icons from 'src/components/Icons';
|
||||||
|
import Label from 'src/components/Label';
|
||||||
|
import { t } from '@superset-ui/core';
|
||||||
|
|
||||||
|
// Define the prop types for DatasetTypeLabel
|
||||||
|
interface DatasetTypeLabelProps {
|
||||||
|
datasetType: 'physical' | 'virtual'; // Accepts only 'physical' or 'virtual'
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIZE = 's'; // Define the size as a constant
|
||||||
|
|
||||||
|
const DatasetTypeLabel: React.FC<DatasetTypeLabelProps> = ({ datasetType }) => {
|
||||||
|
const label: string =
|
||||||
|
datasetType === 'physical' ? t('Physical') : t('Virtual');
|
||||||
|
const icon =
|
||||||
|
datasetType === 'physical' ? (
|
||||||
|
<Icons.Table iconSize={SIZE} />
|
||||||
|
) : (
|
||||||
|
<Icons.ConsoleSqlOutlined iconSize={SIZE} />
|
||||||
|
);
|
||||||
|
const labelType: 'primary' | 'secondary' =
|
||||||
|
datasetType === 'physical' ? 'primary' : 'secondary';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label icon={icon} type={labelType}>
|
||||||
|
{label}
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DatasetTypeLabel;
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* 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 Icons from 'src/components/Icons';
|
||||||
|
import Label from 'src/components/Label';
|
||||||
|
import { t } from '@superset-ui/core';
|
||||||
|
|
||||||
|
// Define props for the PublishedLabel component
|
||||||
|
interface PublishedLabelProps {
|
||||||
|
isPublished: boolean; // Whether the item is published
|
||||||
|
onClick?: () => void; // Optional click handler
|
||||||
|
}
|
||||||
|
|
||||||
|
const PublishedLabel: React.FC<PublishedLabelProps> = ({
|
||||||
|
isPublished,
|
||||||
|
onClick,
|
||||||
|
}) => {
|
||||||
|
const label = isPublished ? t('Published') : t('Draft');
|
||||||
|
const icon = isPublished ? (
|
||||||
|
<Icons.CircleCheck iconSize="s" />
|
||||||
|
) : (
|
||||||
|
<Icons.Minus iconSize="s" />
|
||||||
|
);
|
||||||
|
const labelType = isPublished ? 'primary' : 'secondary';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label type={labelType} icon={icon} onClick={onClick}>
|
||||||
|
{label}
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublishedLabel;
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { styled } from '@superset-ui/core';
|
import { styled } from '@superset-ui/core';
|
||||||
import Label, { Type } from 'src/components/Label';
|
import Label, { Type } from 'src/components/Label';
|
||||||
|
import Icons from 'src/components/Icons';
|
||||||
|
|
||||||
import { now, fDuration } from 'src/utils/dates';
|
import { now, fDuration } from 'src/utils/dates';
|
||||||
|
|
||||||
|
|
@ -68,7 +69,7 @@ export default function Timer({
|
||||||
}, [endTime, isRunning, startTime]);
|
}, [endTime, isRunning, startTime]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TimerLabel type={status} role="timer">
|
<TimerLabel icon={<Icons.Clock iconSize="m" />} type={status} role="timer">
|
||||||
{clockStr}
|
{clockStr}
|
||||||
</TimerLabel>
|
</TimerLabel>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
import { t } from '@superset-ui/core';
|
import { t } from '@superset-ui/core';
|
||||||
import { Tooltip } from 'src/components/Tooltip';
|
import { Tooltip } from 'src/components/Tooltip';
|
||||||
import Label from 'src/components/Label';
|
import { PublishedLabel } from 'src/components/Label';
|
||||||
import { HeaderProps, HeaderDropdownProps } from '../Header/types';
|
import { HeaderProps, HeaderDropdownProps } from '../Header/types';
|
||||||
|
|
||||||
export type DashboardPublishedStatusType = {
|
export type DashboardPublishedStatusType = {
|
||||||
|
|
@ -67,13 +67,12 @@ export default class PublishedStatus extends Component<DashboardPublishedStatusT
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
title={draftButtonTooltip}
|
title={draftButtonTooltip}
|
||||||
>
|
>
|
||||||
<Label
|
<div>
|
||||||
onClick={() => {
|
<PublishedLabel
|
||||||
this.togglePublished();
|
isPublished={isPublished}
|
||||||
}}
|
onClick={this.togglePublished}
|
||||||
>
|
/>
|
||||||
{t('Draft')}
|
</div>
|
||||||
</Label>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +82,9 @@ export default class PublishedStatus extends Component<DashboardPublishedStatusT
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
title={draftDivTooltip}
|
title={draftDivTooltip}
|
||||||
>
|
>
|
||||||
<Label>{t('Draft')}</Label>
|
<div>
|
||||||
|
<PublishedLabel isPublished={isPublished} />
|
||||||
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -96,13 +97,12 @@ export default class PublishedStatus extends Component<DashboardPublishedStatusT
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
title={publishedTooltip}
|
title={publishedTooltip}
|
||||||
>
|
>
|
||||||
<Label
|
<div>
|
||||||
onClick={() => {
|
<PublishedLabel
|
||||||
this.togglePublished();
|
isPublished={isPublished}
|
||||||
}}
|
onClick={this.togglePublished}
|
||||||
>
|
/>
|
||||||
{t('Published')}
|
</div>
|
||||||
</Label>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import {
|
||||||
} from 'src/views/CRUD/utils';
|
} from 'src/views/CRUD/utils';
|
||||||
import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
|
import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
|
||||||
import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
|
import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
|
||||||
|
import { PublishedLabel } from 'src/components/Label';
|
||||||
import { TagsList } from 'src/components/Tags';
|
import { TagsList } from 'src/components/Tags';
|
||||||
import handleResourceExport from 'src/utils/export';
|
import handleResourceExport from 'src/utils/export';
|
||||||
import Loading from 'src/components/Loading';
|
import Loading from 'src/components/Loading';
|
||||||
|
|
@ -343,8 +344,9 @@ function DashboardList(props: DashboardListProps) {
|
||||||
row: {
|
row: {
|
||||||
original: { status },
|
original: { status },
|
||||||
},
|
},
|
||||||
}: any) =>
|
}: any) => (
|
||||||
status === DashboardStatus.PUBLISHED ? t('Published') : t('Draft'),
|
<PublishedLabel isPublished={status === DashboardStatus.PUBLISHED} />
|
||||||
|
),
|
||||||
Header: t('Status'),
|
Header: t('Status'),
|
||||||
accessor: 'published',
|
accessor: 'published',
|
||||||
size: 'xl',
|
size: 'xl',
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import ListView, {
|
||||||
Filters,
|
Filters,
|
||||||
FilterOperator,
|
FilterOperator,
|
||||||
} from 'src/components/ListView';
|
} from 'src/components/ListView';
|
||||||
|
import { DatasetTypeLabel } from 'src/components/Label';
|
||||||
import Loading from 'src/components/Loading';
|
import Loading from 'src/components/Loading';
|
||||||
import SubMenu, { SubMenuProps, ButtonProps } from 'src/features/home/SubMenu';
|
import SubMenu, { SubMenuProps, ButtonProps } from 'src/features/home/SubMenu';
|
||||||
import Owner from 'src/types/Owner';
|
import Owner from 'src/types/Owner';
|
||||||
|
|
@ -279,24 +280,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||||
row: {
|
row: {
|
||||||
original: { kind },
|
original: { kind },
|
||||||
},
|
},
|
||||||
}: any) => {
|
}: any) => null,
|
||||||
if (kind === 'physical') {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
id="physical-dataset-tooltip"
|
|
||||||
title={t('Physical dataset')}
|
|
||||||
>
|
|
||||||
<Icons.DatasetPhysical />
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip id="virtual-dataset-tooltip" title={t('Virtual dataset')}>
|
|
||||||
<Icons.DatasetVirtual />
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
accessor: 'kind_icon',
|
accessor: 'kind_icon',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
size: 'xs',
|
size: 'xs',
|
||||||
|
|
@ -360,7 +344,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||||
row: {
|
row: {
|
||||||
original: { kind },
|
original: { kind },
|
||||||
},
|
},
|
||||||
}: any) => (kind === 'physical' ? t('Physical') : t('Virtual')),
|
}: any) => <DatasetTypeLabel datasetType={kind} />,
|
||||||
Header: t('Type'),
|
Header: t('Type'),
|
||||||
accessor: 'kind',
|
accessor: 'kind',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue