Skip to content
Snippets Groups Projects
Unverified Commit 214a5989 authored by Romeo Van Snick's avatar Romeo Van Snick Committed by GitHub
Browse files

Info icon design update (#42557)

* Render item icon inline with column icon

* Use inline info icon in field picker

* Use new QueryColumnInfoIcon on filter columns picker

* Use new QueryColumnInfoIcon on suggestions dropdown

* Use new QueryColumnInfoIcon aggregations dropdown

* Use new QueryColumnInfoIcon filter modal

* Use new QueryColumnInfoIcon in BreakoutColumnListItem

* Use new QueryColumnInfoIcon in FieldList

* Use new QueryColumnInfoIcon in DataSelectorFieldPicker

* Update TableInfoIcon to render fallback too

* Use new TableInfoIcon in DataSelector

* Use new TableInfoIcon in ViewHeader

* Fix types in breakout column

* Move aria-label to wrapper

* Move binning back to the right

* Always give info icon full opacity

* Remove asDot props everywhere
parent 90f7e62f
No related merge requests found
Showing
with 148 additions and 157 deletions
import styled from "@emotion/styled";
import {
QueryColumnInfoIcon as _QueryColumnInfoIcon,
QueryColumnInfoIcon,
HoverParent,
} from "metabase/components/MetadataInfo/ColumnInfoIcon";
import { color, alpha, darken } from "metabase/lib/colors";
import { Icon } from "metabase/ui";
import { color } from "metabase/lib/colors";
export const ItemTitle = styled.div`
min-width: 10ch;
`;
export const ItemIcon = styled(Icon)`
export const ItemIcon = styled(QueryColumnInfoIcon)`
margin: 0 0.5em;
margin-left: 0.75em;
color: ${color("text-dark")};
`;
export const QueryColumnInfoIcon = styled(_QueryColumnInfoIcon)`
color: ${alpha(darken(color("brand"), 0.6), 0.8)};
margin-left: auto;
`;
export const ItemList = styled.ul`
padding: 0.5em;
`;
......
import { useMemo } from "react";
import { t } from "ttag";
import { getColumnIcon } from "metabase/common/utils/columns";
import { Checkbox, DelayGroup } from "metabase/ui";
import * as Lib from "metabase-lib";
......@@ -11,7 +10,6 @@ import {
Label,
ItemTitle,
ItemIcon,
QueryColumnInfoIcon,
} from "./FieldPicker.styled";
interface FieldPickerProps {
......@@ -89,15 +87,14 @@ export const FieldPicker = ({
}
onChange={event => onToggle(item.column, event.target.checked)}
/>
<ItemIcon name={getColumnIcon(item.column)} size={18} />
<ItemTitle>{item.columnInfo.displayName}</ItemTitle>
<QueryColumnInfoIcon
<ItemIcon
query={query}
stageIndex={stageIndex}
column={item.column}
position="right"
position="top-start"
size={18}
/>
<ItemTitle>{item.columnInfo.displayName}</ItemTitle>
</Label>
</li>
))}
......
......@@ -20,7 +20,7 @@ export const ChevronDown = styled(Icon)`
opacity: 0.75;
`;
export const TriggerButton = styled.button<{ hasDot?: boolean }>`
export const TriggerButton = styled.button`
display: flex;
align-items: center;
min-width: 0;
......@@ -29,8 +29,8 @@ export const TriggerButton = styled.button<{ hasDot?: boolean }>`
font-weight: 700;
border-left: 2px solid transparent;
padding: 0.5rem;
border-left: 2px solid
${props => (props.hasDot ? "transparent" : alpha(color("border"), 0.1))};
border-left: 2px solid ${alpha(color("border"), 0.1)};
margin-left: auto;
cursor: pointer;
......
......@@ -11,7 +11,6 @@ import * as Lib from "metabase-lib";
import {
Content,
ChevronDown,
Dot,
MoreButton,
SelectListItem,
TriggerButton,
......@@ -37,7 +36,6 @@ export interface BaseBucketPickerPopoverProps {
isEditing: boolean;
triggerLabel?: string;
hasArrowIcon?: boolean;
hasDot?: boolean;
hasChevronDown?: boolean;
color?: ColorName;
checkBucketIsSelected: (item: BucketListItem) => boolean;
......@@ -57,7 +55,6 @@ function _BaseBucketPickerPopover({
checkBucketIsSelected,
renderTriggerContent,
onSelect,
hasDot,
hasChevronDown,
}: BaseBucketPickerPopoverProps) {
const [isOpened, setIsOpened] = useState(false);
......@@ -101,7 +98,6 @@ function _BaseBucketPickerPopover({
<Popover.Target>
<TriggerButton
aria-label={triggerLabel}
hasDot={hasDot}
// Compat with E2E tests around MLv1-based components
// Prefer using a11y role selectors
data-testid="dimension-list-item-binning"
......@@ -110,7 +106,6 @@ function _BaseBucketPickerPopover({
setIsOpened(!isOpened);
}}
>
{hasDot && <Dot />}
<Ellipsified>
{renderTriggerContent(triggerContentBucketDisplayInfo)}
</Ellipsified>
......
......@@ -9,7 +9,6 @@ type CommonProps = Pick<
| "isEditing"
| "color"
| "hasArrowIcon"
| "hasDot"
| "hasChevronDown"
>;
......
import styled from "@emotion/styled";
import { QueryColumnInfoIcon } from "metabase/components/MetadataInfo/ColumnInfoIcon";
import AccordionList from "metabase/core/components/AccordionList";
import { color } from "metabase/lib/colors";
import type { ColorName } from "metabase/lib/colors/types";
......@@ -24,7 +23,3 @@ export const ColumnNameContainer = styled.div`
overflow: hidden;
}
`;
export const ColumnInfoIcon = styled(QueryColumnInfoIcon)`
align-self: center;
`;
......@@ -5,16 +5,17 @@ import {
getColumnGroupIcon,
getColumnGroupName,
} from "metabase/common/utils/column-groups";
import { getColumnIcon } from "metabase/common/utils/columns";
import { HoverParent } from "metabase/components/MetadataInfo/ColumnInfoIcon";
import {
QueryColumnInfoIcon,
HoverParent,
} from "metabase/components/MetadataInfo/ColumnInfoIcon";
import type { ColorName } from "metabase/lib/colors/types";
import type { IconName } from "metabase/ui";
import { Box, DelayGroup, Icon } from "metabase/ui";
import { Box, DelayGroup } from "metabase/ui";
import * as Lib from "metabase-lib";
import { BucketPickerPopover } from "./BucketPickerPopover";
import {
ColumnInfoIcon,
ColumnNameContainer,
StyledAccordionList,
} from "./QueryColumnPicker.styled";
......@@ -154,7 +155,6 @@ export function QueryColumnPicker({
isEditing={checkIsColumnSelected(item)}
hasBinning={hasBinning}
hasTemporalBucketing={hasTemporalBucketing}
hasDot={withInfoIcons}
hasChevronDown={withInfoIcons}
color={color}
onSelect={handleSelect}
......@@ -174,13 +174,13 @@ export function QueryColumnPicker({
],
);
const renderItemExtra = useCallback(
const renderItemIcon = useCallback(
(item: ColumnListItem) => (
<ColumnInfoIcon
<QueryColumnInfoIcon
query={query}
stageIndex={stageIndex}
column={item.column}
position="right"
position="top-start"
/>
),
[query, stageIndex],
......@@ -198,7 +198,6 @@ export function QueryColumnPicker({
renderItemName={renderItemName}
renderItemDescription={omitItemDescription}
renderItemIcon={renderItemIcon}
renderItemExtra={renderItemExtra}
renderItemLabel={renderItemLabel}
color={color}
maxHeight={Infinity}
......@@ -228,7 +227,3 @@ function renderItemWrapper(content: ReactNode) {
function omitItemDescription() {
return null;
}
function renderItemIcon(item: ColumnListItem) {
return <Icon name={getColumnIcon(item.column)} size={18} />;
}
import { t } from "ttag";
import { getColumnIcon } from "metabase/common/utils/columns";
import type { IconName } from "metabase/ui";
import * as Lib from "metabase-lib";
import type {
......@@ -10,55 +12,76 @@ import {
QueryColumnInfoPopover,
TableColumnInfoPopover,
} from "../ColumnInfoPopover";
import { PopoverHoverTarget, HoverParent } from "../InfoIcon";
import {
PopoverHoverTarget,
PopoverDefaultIcon,
HoverParent,
} from "../InfoIcon";
export { HoverParent };
type QueryColumnInfoIconProps = QueryColumnInfoPopoverProps & {
size?: number;
icon?: IconName;
};
export function QueryColumnInfoIcon({
className,
delay,
size,
icon,
...props
}: QueryColumnInfoPopoverProps) {
}: QueryColumnInfoIconProps) {
const { query, stageIndex, column } = props;
const { description = "" } = query
? Lib.displayInfo(query, stageIndex, column)
: {};
if (!description) {
return null;
}
return (
<QueryColumnInfoPopover {...props} delay={delay}>
<PopoverHoverTarget
className={className}
name="info_filled"
hasDescription={Boolean(description)}
aria-label={t`More info`}
/>
</QueryColumnInfoPopover>
<>
<QueryColumnInfoPopover {...props} delay={delay}>
<span aria-label={t`More info`}>
<PopoverDefaultIcon
className={className}
name={icon ?? getColumnIcon(column)}
size={size}
/>
<PopoverHoverTarget
className={className}
name="info_filled"
hasDescription={Boolean(description)}
size={size}
/>
</span>
</QueryColumnInfoPopover>
</>
);
}
QueryColumnInfoIcon.HoverParent = HoverParent;
type TableColumnInfoIconProps = TableColumnInfoPopoverProps & {
icon: IconName;
size?: number;
};
export function TableColumnInfoIcon({
className,
delay,
field,
icon,
size,
...props
}: TableColumnInfoPopoverProps) {
if (!field.description) {
return null;
}
}: TableColumnInfoIconProps) {
return (
<TableColumnInfoPopover {...props} field={field} delay={delay}>
<PopoverHoverTarget
className={className}
name="info_filled"
hasDescription={Boolean(field.description)}
aria-label={t`More info`}
/>
<span aria-label={t`More info`}>
<PopoverDefaultIcon className={className} name={icon} size={size} />
<PopoverHoverTarget
className={className}
name="info_filled"
hasDescription={Boolean(field.description)}
/>
</span>
</TableColumnInfoPopover>
);
}
......
......@@ -3,18 +3,28 @@ import styled from "@emotion/styled";
import { Icon } from "metabase/ui";
export const PopoverHoverTarget = styled(Icon)<{ hasDescription: boolean }>`
padding: 0.7em 0.65em;
visibility: hidden;
flex-shrink: 0;
opacity: ${props => (props.hasDescription ? 0.6 : 0.3)};
display: none;
&[aria-expanded="true"] {
opacity: 1;
[aria-expanded="true"] & {
display: block;
}
`;
export const PopoverDefaultIcon = styled(Icon)`
display: block;
[aria-expanded="true"] & {
display: none;
}
`;
export const HoverParent = styled.div`
&:hover ${PopoverHoverTarget} {
visibility: visible;
display: block;
}
&:hover ${PopoverDefaultIcon} {
display: none;
}
`;
import { t } from "ttag";
import { PopoverHoverTarget, HoverParent } from "../InfoIcon";
import type { IconName } from "metabase/ui";
import {
PopoverHoverTarget,
HoverParent,
PopoverDefaultIcon,
} from "../InfoIcon";
import type { TableInfoPopoverProps } from "../TableInfoPopover";
import { TableInfoPopover } from "../TableInfoPopover";
export { HoverParent };
type TableInfoIconProps = TableInfoPopoverProps & {
className?: string;
icon?: IconName;
size?: number;
};
export function TableInfoIcon({
className,
delay,
table,
size,
icon = "table",
...props
}: TableInfoIconProps) {
return (
<TableInfoPopover {...props} table={table} delay={delay}>
<PopoverHoverTarget
className={className}
name="info_filled"
hasDescription={Boolean(table.description)}
aria-label={t`More info`}
/>
<span aria-label={t`More info`}>
<PopoverDefaultIcon name={icon} className={className} size={size} />
<PopoverHoverTarget
className={className}
name="info_filled"
hasDescription={Boolean(table.description)}
size={size}
/>
</span>
</TableInfoPopover>
);
}
......@@ -72,9 +72,11 @@ const DataSelectorFieldPicker = ({
const renderItemIcon = (item: FieldWithName) =>
item.field && (
<Icon
name={item.field.dimension().icon() as unknown as IconName}
<TableColumnInfoIcon
field={item.field}
position="top-end"
size={18}
icon={item.field.dimension().icon() as unknown as IconName}
/>
);
......@@ -95,7 +97,6 @@ const DataSelectorFieldPicker = ({
itemIsClickable={(item: FieldWithName) => item.field}
renderItemWrapper={renderItemWrapper}
renderItemIcon={renderItemIcon}
renderItemExtra={renderItemExtra}
/>
</DelayGroup>
</Container>
......@@ -106,10 +107,6 @@ function renderItemWrapper(content: ReactNode) {
return <HoverParent>{content}</HoverParent>;
}
function renderItemExtra(item: FieldWithName) {
return <TableColumnInfoIcon field={item.field} position="top-end" />;
}
const Header = ({ onBack, selectedTable }: HeaderProps) => (
<HeaderContainer onClick={onBack}>
<Icon name="chevronleft" size={18} />
......
......@@ -95,10 +95,7 @@ const DataSelectorTablePicker = ({
table && selectedTable ? table.id === selectedTable.id : false;
const renderItemIcon = ({ table }: { table: Table }) =>
table ? <Icon name="table" /> : null;
const renderItemExtra = ({ table }: { table: Table }) =>
table && <TableInfoIcon table={table} position="right" />;
table ? <TableInfoIcon table={table} position="top-start" /> : null;
const renderItemWrapper = (content: ReactNode) => (
<HoverParent>{content}</HoverParent>
......@@ -127,7 +124,6 @@ const DataSelectorTablePicker = ({
showSpinner={showSpinner}
itemIsSelected={checkIfItemIsSelected}
itemIsClickable={checkIfItemIsClickable}
renderItemExtra={renderItemExtra}
renderItemIcon={renderItemIcon}
renderItemWrapper={renderItemWrapper}
showItemArrows={hasNextStep}
......
import { t, ngettext, msgid } from "ttag";
import { ngettext, msgid } from "ttag";
import type { IconName } from "metabase/ui";
import { DelayGroup } from "metabase/ui";
......@@ -8,7 +8,6 @@ import {
NodeListItem,
NodeListItemLink,
NodeListItemName,
NodeListItemIcon,
NodeListTitle,
NodeListContainer,
NodeListIcon,
......@@ -38,13 +37,11 @@ const FieldList = ({ fields, onFieldClick }: FieldListProps) => (
// field.icon() cannot be annotated to return IconName
// because metabase-lib cannot import from metabase.
const iconName = field.icon() as IconName;
const tooltip = iconName === "unknown" ? t`Unknown type` : null;
return (
<NodeListItem as="li" key={field.getUniqueId()}>
<NodeListItemLink onClick={() => onFieldClick(field)}>
<NodeListItemIcon name={iconName} tooltip={tooltip} />
<NodeListInfoIcon field={field} position="left" icon={iconName} />
<NodeListItemName>{field.name}</NodeListItemName>
<NodeListInfoIcon field={field} position="top-end" />
</NodeListItemLink>
</NodeListItem>
);
......
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import { QueryColumnInfoIcon as BaseQueryColumnInfoIcon } from "metabase/components/MetadataInfo/ColumnInfoIcon";
import {
HoverParent,
PopoverHoverTarget as BasePopoverHoverTarget,
......@@ -71,12 +70,6 @@ export const SuggestionTitle = styled.span`
margin-right: 1.5em;
`;
export const QueryColumnInfoIcon = styled(BaseQueryColumnInfoIcon)`
padding: 0;
margin-left: auto;
padding: 0.3125rem 0;
`;
export const PopoverHoverTarget = styled(BasePopoverHoverTarget)`
padding: 0;
margin-left: auto;
......
......@@ -9,6 +9,7 @@ import {
import { t } from "ttag";
import _ from "underscore";
import { QueryColumnInfoIcon } from "metabase/components/MetadataInfo/ColumnInfoIcon";
import { HoverParent } from "metabase/components/MetadataInfo/InfoIcon";
import { Popover as InfoPopover } from "metabase/components/MetadataInfo/Popover";
import CS from "metabase/css/core/index.css";
......@@ -42,7 +43,6 @@ import {
SuggestionMatch,
SuggestionTitle,
GroupTitle,
QueryColumnInfoIcon,
PopoverHoverTarget,
} from "./ExpressionEditorSuggestions.styled";
......@@ -247,13 +247,22 @@ function ExpressionEditorSuggestionsListItem({
className={cx(CS.hoverParent, CS.hoverInherit)}
data-testid="expression-suggestions-list-item"
>
{icon && (
{icon && (helpText || !suggestion.column) && (
<Icon
name={icon as IconName}
color={isHighlighted ? highlighted : normal}
className={CS.mr1}
/>
)}
{!helpText && suggestion.column && (
<QueryColumnInfoIcon
query={query}
stageIndex={stageIndex}
column={suggestion.column}
position="top-start"
className={CS.mr1}
/>
)}
<SuggestionTitle>
{suggestion.name.slice(0, start)}
<SuggestionMatch>{suggestion.name.slice(start, end)}</SuggestionMatch>
......@@ -272,14 +281,6 @@ function ExpressionEditorSuggestionsListItem({
/>
</InfoPopover>
)}
{!helpText && suggestion.column && (
<QueryColumnInfoIcon
query={query}
stageIndex={stageIndex}
column={suggestion.column}
position="right"
/>
)}
</ExpressionListItem>
</HoverParent>
);
......
......@@ -2,6 +2,7 @@ import PropTypes from "prop-types";
import { isValidElement } from "react";
import { t } from "ttag";
import { TableInfoIcon } from "metabase/components/MetadataInfo/TableInfoIcon/TableInfoIcon";
import Tooltip from "metabase/core/components/Tooltip";
import Collections from "metabase/entities/collections";
import Questions from "metabase/entities/questions";
......@@ -18,7 +19,7 @@ import * as ML_Urls from "metabase-lib/v1/urls";
import { HeadBreadcrumbs } from "../HeaderBreadcrumbs";
import { TablesDivider, TableInfoIcon } from "./QuestionDataSource.styled";
import { TablesDivider, IconWrapper } from "./QuestionDataSource.styled";
QuestionDataSource.propTypes = {
question: PropTypes.object,
......@@ -260,7 +261,16 @@ function QuestionTableBadges({ tables, subHead, hasLink, isLast }) {
>
<span>
{table.displayName()}
{!subHead && <TableInfoIcon table={table} />}
{!subHead && (
<IconWrapper>
<TableInfoIcon
table={table}
icon="info_filled"
size={12}
position="bottom"
/>
</IconWrapper>
)}
</span>
</HeadBreadcrumbs.Badge>
));
......
......@@ -11,12 +11,10 @@ export const TablesDivider = styled.span`
user-select: none;
`;
export const TableInfoIcon = styled(_TableInfoIcon)`
export const IconWrapper = styled.span`
color: ${color("text-light")};
visibility: visible;
font-size: min(1rem, 1em);
padding: 0;
margin-left: 0.5em;
position: relative;
top: 1px;
display: inline-block;
font-size: 1rem;
margin-left: 0.5rem;
vertical-align: middle;
`;
......@@ -2,10 +2,9 @@ import { css } from "@emotion/react";
import styled from "@emotion/styled";
import { BucketPickerPopover } from "metabase/common/components/QueryColumnPicker/BucketPickerPopover";
import { QueryColumnInfoIcon as BaseQueryColumnInfoIcon } from "metabase/components/MetadataInfo/ColumnInfoIcon";
import { QueryColumnInfoIcon } from "metabase/components/MetadataInfo/ColumnInfoIcon";
import Button from "metabase/core/components/Button";
import { color, alpha } from "metabase/lib/colors";
import { Icon } from "metabase/ui";
export const Content = styled.div`
display: flex;
......@@ -55,7 +54,7 @@ AddButton.defaultProps = {
borderless: true,
};
export const ColumnTypeIcon = styled(Icon)`
export const ColumnTypeIcon = styled(QueryColumnInfoIcon)`
color: ${color("text-medium")};
`;
......@@ -66,14 +65,9 @@ export const Title = styled.div`
font-weight: 700;
`;
export const QueryColumnInfoIcon = styled(BaseQueryColumnInfoIcon)`
margin-left: auto;
`;
const selectedStyle = css`
${Content},
${ColumnTypeIcon},
${QueryColumnInfoIcon} {
${ColumnTypeIcon} {
background-color: ${color("summarize")};
color: ${color("white")};
}
......@@ -81,8 +75,6 @@ const selectedStyle = css`
${BucketPickerPopover.TriggerButton} {
opacity: 1;
color: ${alpha("white", 0.65)};
padding-left: 0;
border-left: 0;
}
${BucketPickerPopover.TriggerButton}:hover {
......@@ -95,11 +87,9 @@ const unselectedStyle = css`
${BucketPickerPopover.TriggerButton} {
opacity: 0;
color: ${color("text-light")};
padding-left: 0;
border-left: 0;
}
${QueryColumnInfoIcon} {
${ColumnTypeIcon} {
color: ${color("text-light")};
}
......
......@@ -3,7 +3,6 @@ import { useCallback } from "react";
import { t } from "ttag";
import { BucketPickerPopover } from "metabase/common/components/QueryColumnPicker/BucketPickerPopover";
import { getColumnIcon } from "metabase/common/utils/columns";
import { HoverParent } from "metabase/components/MetadataInfo/ColumnInfoIcon";
import Tooltip from "metabase/core/components/Tooltip";
import * as Lib from "metabase-lib";
......@@ -16,7 +15,6 @@ import {
TitleContainer,
RemoveButton,
Root,
QueryColumnInfoIcon,
} from "./BreakoutColumnListItem.styled";
const STAGE_INDEX = -1;
......@@ -72,7 +70,13 @@ export function BreakoutColumnListItem({
>
<Content onClick={handleListItemClick}>
<TitleContainer>
<ColumnTypeIcon name={getColumnIcon(item.column)} size={18} />
<ColumnTypeIcon
query={query}
stageIndex={STAGE_INDEX}
column={item.column}
position="left"
size={18}
/>
<Title data-testid="dimension-list-item-name">{displayName}</Title>
<BucketPickerPopover
query={query}
......@@ -80,7 +84,6 @@ export function BreakoutColumnListItem({
column={item.column}
color="summarize"
isEditing={isSelected}
hasDot
hasChevronDown
hasBinning
hasTemporalBucketing
......@@ -88,12 +91,6 @@ export function BreakoutColumnListItem({
breakout ? onUpdateColumn(column) : onAddColumn(column)
}
/>
<QueryColumnInfoIcon
query={query}
stageIndex={STAGE_INDEX}
column={item.column}
position="top-end"
/>
</TitleContainer>
{isSelected && (
<RemoveButton
......
import styled from "@emotion/styled";
export const InfoIconWrapper = styled.div`
position: relative;
left: 0.25rem;
width: 2rem;
display: flex;
align-items: center;
`;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment