Skip to content
Snippets Groups Projects
Unverified Commit b334ccd6 authored by Alexander Polyankin's avatar Alexander Polyankin Committed by GitHub
Browse files

Show inactive tables in search results & recent items (#18743)

parent 3fdf8e0d
No related merge requests found
......@@ -61,7 +61,14 @@ export default createEntity({
: [],
};
} else {
return searchList(query);
const { data, ...rest } = await searchList(query);
return {
...rest,
// TODO Alexander Polyankin 11/09/21
// Until BE returns databases and tables before the initial sync, set it to true to unblock FE changes
data: data?.map(item => ({ ...item, initial_sync: true })),
};
}
},
},
......
import React, { useState } from "react";
import PropTypes from "prop-types";
import { t, jt } from "ttag";
import { jt, t } from "ttag";
import Link from "metabase/components/Link";
import { Box, Flex } from "grid-styled";
......@@ -20,7 +20,6 @@ import PaginationControls from "metabase/components/PaginationControls";
import { usePagination } from "metabase/hooks/use-pagination";
const PAGE_PADDING = [1, 2, 4];
const PAGE_SIZE = 50;
const SEARCH_FILTERS = [
......
......@@ -9,6 +9,7 @@ import Text from "metabase/components/type/Text";
import * as Urls from "metabase/lib/urls";
import {
ResultLink,
ResultSpinner,
Title,
} from "metabase/search/components/SearchResult.styled";
import { ItemIcon } from "metabase/search/components/SearchResult";
......@@ -24,10 +25,6 @@ import {
const LOADER_THRESHOLD = 100;
const getItemKey = ({ model, model_id }) => `${model}:${model_id}`;
const getItemName = model_object =>
model_object.display_name || model_object.name;
const propTypes = {
list: PropTypes.arrayOf(
PropTypes.shape({
......@@ -59,26 +56,43 @@ function RecentsList({ list, loading }) {
<React.Fragment>
{hasRecents && (
<ul>
{list.map(item => (
<li key={getItemKey(item)}>
<ResultLink to={Urls.modelToUrl(item)} compact={true}>
<RecentListItemContent
align="start"
data-testid="recently-viewed-item"
>
<ItemIcon item={item} type={item.model} />
<div>
<Title data-testid="recently-viewed-item-title">
{getItemName(item.model_object)}
</Title>
<Text data-testid="recently-viewed-item-type">
{getTranslatedEntityName(item.model)}
</Text>
</div>
</RecentListItemContent>
</ResultLink>
</li>
))}
{list.map(item => {
const key = getItemKey(item);
const title = getItemName(item);
const type = getTranslatedEntityName(item.model);
const active = isItemActive(item);
const loading = isItemLoading(item);
const url = active ? Urls.modelToUrl(item) : "";
return (
<li key={key}>
<ResultLink to={url} compact={true} active={active}>
<RecentListItemContent
align="start"
data-testid="recently-viewed-item"
>
<ItemIcon
item={item}
type={item.model}
active={active}
/>
<div>
<Title
active={active}
data-testid="recently-viewed-item-title"
>
{title}
</Title>
<Text data-testid="recently-viewed-item-type">
{type}
</Text>
</div>
{loading && <ResultSpinner size={24} borderWidth={3} />}
</RecentListItemContent>
</ResultLink>
</li>
);
})}
</ul>
)}
......@@ -95,6 +109,33 @@ function RecentsList({ list, loading }) {
RecentsList.propTypes = propTypes;
const getItemKey = ({ model, model_id }) => {
return `${model}:${model_id}`;
};
const getItemName = ({ model_object }) => {
return model_object.display_name || model_object.name;
};
const isItemActive = ({ model, model_object }) => {
switch (model) {
case "table":
return model_object.initial_sync;
default:
return true;
}
};
const isItemLoading = ({ model, model_object }) => {
switch (model) {
case "database":
case "table":
return !model_object.initial_sync;
default:
return false;
}
};
export default _.compose(
Recents.loadList({
wrapped: true,
......
......@@ -119,6 +119,15 @@ CollectionSchema.define({
export const RecentsSchema = new schema.Entity("recents", undefined, {
idAttribute: ({ model, model_id }) => `${model}:${model_id}`,
processStrategy(item) {
// TODO Alexander Polyankin 11/05/21
// Until BE returns tables before the initial sync, set it to true to unblock FE changes
if (item.model_object) {
item.model_object.initial_sync = true;
}
return item;
},
});
export const LoginHistorySchema = new schema.Entity("loginHistory", undefined, {
......
......@@ -17,6 +17,7 @@ import {
Description,
ContextText,
ContextContainer,
ResultSpinner,
} from "./SearchResult.styled";
import { InfoText } from "./InfoText";
......@@ -47,10 +48,10 @@ function DefaultIcon({ item }) {
return <Icon {...item.getIcon()} size={DEFAULT_ICON_SIZE} />;
}
export function ItemIcon({ item, type }) {
export function ItemIcon({ item, type, active }) {
const IconComponent = ModelIconComponentMap[type] || DefaultIcon;
return (
<IconWrapper item={item} type={type}>
<IconWrapper item={item} type={type} active={active}>
<IconComponent item={item} />
</IconWrapper>
);
......@@ -93,25 +94,24 @@ export default function SearchResult({
hasDescription,
onClick,
}) {
const linkProps = {};
if (typeof onClick === "function") {
linkProps.onClick = () => onClick(result);
} else {
linkProps.to = result.getUrl();
}
const active = isItemActive(result);
const loading = isItemLoading(result);
return (
<ResultLink
{...linkProps}
active={active}
compact={compact}
to={!onClick ? result.getUrl() : ""}
onClick={onClick ? () => onClick(result) : undefined}
data-testid="search-result-item"
>
<Flex align="start">
<ItemIcon item={result} type={result.model} />
<ItemIcon item={result} type={result.model} active={active} />
<Box>
<TitleWrapper>
<Title data-testid="search-result-item-name">{result.name}</Title>
<Title active={active} data-testid="search-result-item-name">
{result.name}
</Title>
<PLUGIN_MODERATION.ModerationStatusIcon
status={result.moderated_status}
size={12}
......@@ -125,8 +125,28 @@ export default function SearchResult({
)}
<Score scores={result.scores} />
</Box>
{loading && <ResultSpinner size={24} borderWidth={3} />}
</Flex>
{compact || <Context context={result.context} />}
</ResultLink>
);
}
const isItemActive = result => {
switch (result.model) {
case "table":
return result.initial_sync;
default:
return true;
}
};
const isItemLoading = result => {
switch (result.model) {
case "database":
case "table":
return !result.initial_sync;
default:
return false;
}
};
import styled from "styled-components";
import { color, lighten } from "metabase/lib/colors";
import { space } from "metabase/styled-components/theme";
import Link from "metabase/components/Link";
import Text from "metabase/components/type/Text";
import { space } from "metabase/styled-components/theme";
import LoadingSpinner from "metabase/components/LoadingSpinner";
function getColorForIconWrapper(props) {
if (props.item.collection_position) {
if (!props.active) {
return color("text-medium");
} else if (props.item.collection_position) {
return color("saturated-yellow");
} else if (props.type === "collection") {
return lighten("brand", 0.35);
} else {
return color("brand");
}
return props.type === "collection" ? lighten("brand", 0.35) : color("brand");
}
export const IconWrapper = styled.div`
......@@ -24,6 +29,23 @@ export const IconWrapper = styled.div`
flex-shrink: 0;
`;
export const TitleWrapper = styled.div`
display: flex;
grid-gap: 0.25rem;
align-items: center;
`;
export const ContextText = styled("p")`
line-height: 1.4em;
color: ${color("text-medium")};
margin-top: 0;
`;
export const Title = styled("h3")`
margin-bottom: 4px;
color: ${props => color(props.active ? "text-dark" : "text-medium")};
`;
export const ResultLink = styled(Link)`
display: block;
background-color: transparent;
......@@ -32,12 +54,13 @@ export const ResultLink = styled(Link)`
padding-bottom: ${space(1)};
padding-left: 14px;
padding-right: ${props => (props.compact ? "20px" : space(3))};
cursor: ${props => (props.active ? "pointer" : "default")};
&:hover {
background-color: ${lighten("brand", 0.63)};
background-color: ${props => (props.acitve ? lighten("brand", 0.63) : "")};
h3 {
color: ${color("brand")};
color: ${props => (props.active ? color("brand") : "")};
}
}
......@@ -45,9 +68,10 @@ export const ResultLink = styled(Link)`
text-underline-position: under;
text-decoration: underline ${color("text-light")};
text-decoration-style: dashed;
&:hover {
color: ${color("brand")};
text-decoration-color: ${color("brand")};
color: ${props => (props.active ? color("brand") : "")};
text-decoration-color: ${props => (props.active ? color("brand") : "")};
}
}
......@@ -70,22 +94,6 @@ export const ResultLink = styled(Link)`
}
`;
export const TitleWrapper = styled.div`
display: flex;
grid-gap: 0.25rem;
align-items: center;
`;
export const ContextText = styled("p")`
line-height: 1.4em;
color: ${color("text-medium")};
margin-top: 0;
`;
export const Title = styled("h3")`
margin-bottom: 4px;
`;
export const Description = styled(Text)`
padding-left: ${space(1)};
margin-top: ${space(1)} !important;
......@@ -97,3 +105,12 @@ export const ContextContainer = styled.div`
margin-top: 12px;
max-width: 620px;
`;
export const ResultSpinner = styled(LoadingSpinner)`
display: flex;
flex-grow: 1;
align-self: center;
justify-content: flex-end;
margin-left: ${space(1)};
color: ${color("brand")};
`;
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