mirror of
https://github.com/datahub-project/datahub.git
synced 2025-11-10 00:11:15 +00:00
feat(search): embedded view dropdown (#8598)
This commit is contained in:
parent
d3fd025b11
commit
0d03f4b7bb
@ -20,6 +20,13 @@ export const ANTD_GRAY = {
|
|||||||
9: '#434343',
|
9: '#434343',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ANTD_GRAY_V2 = {
|
||||||
|
2: '#F3F5F6',
|
||||||
|
5: '#DDE0E4',
|
||||||
|
8: '#5E666E',
|
||||||
|
10: '#1B1E22',
|
||||||
|
};
|
||||||
|
|
||||||
export const EMPTY_MESSAGES = {
|
export const EMPTY_MESSAGES = {
|
||||||
documentation: {
|
documentation: {
|
||||||
title: 'No documentation yet',
|
title: 'No documentation yet',
|
||||||
|
|||||||
@ -33,6 +33,17 @@ const MenuButton = styled(MoreOutlined)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const MenuStyled = styled(Menu)`
|
||||||
|
&&& {
|
||||||
|
.ant-dropdown-menu-item:not(:hover) {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
.ant-dropdown-menu-item:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const DEFAULT_VIEW_BUILDER_STATE = {
|
const DEFAULT_VIEW_BUILDER_STATE = {
|
||||||
mode: ViewBuilderMode.EDITOR,
|
mode: ViewBuilderMode.EDITOR,
|
||||||
visible: false,
|
visible: false,
|
||||||
@ -233,7 +244,7 @@ export const ViewDropdownMenu = ({
|
|||||||
<>
|
<>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<MenuStyled>
|
||||||
{(canManageView && <EditViewItem key="0" onClick={onEditView} />) || (
|
{(canManageView && <EditViewItem key="0" onClick={onEditView} />) || (
|
||||||
<PreviewViewItem key="0" onClick={onPreviewView} />
|
<PreviewViewItem key="0" onClick={onPreviewView} />
|
||||||
)}
|
)}
|
||||||
@ -247,7 +258,7 @@ export const ViewDropdownMenu = ({
|
|||||||
<SetGlobalDefaultItem key="2" onClick={() => setGlobalDefault(view.urn)} />
|
<SetGlobalDefaultItem key="2" onClick={() => setGlobalDefault(view.urn)} />
|
||||||
)}
|
)}
|
||||||
{canManageView && <DeleteViewItem key="3" onClick={confirmDeleteView} />}
|
{canManageView && <DeleteViewItem key="3" onClick={confirmDeleteView} />}
|
||||||
</Menu>
|
</MenuStyled>
|
||||||
}
|
}
|
||||||
trigger={[trigger]}
|
trigger={[trigger]}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ type Props = {
|
|||||||
|
|
||||||
export const ViewOptionName = ({ name, description }: Props) => {
|
export const ViewOptionName = ({ name, description }: Props) => {
|
||||||
return (
|
return (
|
||||||
<Tooltip placement="left" title={<ViewOptionTooltipTitle name={name} description={description} />}>
|
<Tooltip placement="bottom" showArrow title={<ViewOptionTooltipTitle name={name} description={description} />}>
|
||||||
<ViewName>{name}</ViewName>
|
<ViewName>{name}</ViewName>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,24 +1,18 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { VscTriangleDown } from 'react-icons/vsc';
|
||||||
import { useListMyViewsQuery, useListGlobalViewsQuery } from '../../../../graphql/view.generated';
|
import { useListMyViewsQuery, useListGlobalViewsQuery } from '../../../../graphql/view.generated';
|
||||||
import { useUserContext } from '../../../context/useUserContext';
|
import { useUserContext } from '../../../context/useUserContext';
|
||||||
import { DataHubView, DataHubViewType } from '../../../../types.generated';
|
import { DataHubView, DataHubViewType } from '../../../../types.generated';
|
||||||
import { ViewBuilder } from '../builder/ViewBuilder';
|
import { ViewBuilder } from '../builder/ViewBuilder';
|
||||||
import { DEFAULT_LIST_VIEWS_PAGE_SIZE } from '../utils';
|
import { DEFAULT_LIST_VIEWS_PAGE_SIZE } from '../utils';
|
||||||
import { PageRoutes } from '../../../../conf/Global';
|
import { PageRoutes } from '../../../../conf/Global';
|
||||||
import { ViewSelectToolTip } from './ViewSelectToolTip';
|
|
||||||
import { ViewBuilderMode } from '../builder/types';
|
import { ViewBuilderMode } from '../builder/types';
|
||||||
import { ViewSelectDropdown } from './ViewSelectDropdown';
|
import { ViewSelectDropdown } from './ViewSelectDropdown';
|
||||||
import { renderViewOptionGroup } from './renderViewOptionGroup';
|
import { renderViewOptionGroup } from './renderViewOptionGroup';
|
||||||
|
import { ANTD_GRAY_V2 } from '../../shared/constants';
|
||||||
const selectStyle = {
|
|
||||||
width: 240,
|
|
||||||
};
|
|
||||||
|
|
||||||
const dropdownStyle = {
|
|
||||||
position: 'fixed',
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
type ViewBuilderDisplayState = {
|
type ViewBuilderDisplayState = {
|
||||||
mode: ViewBuilderMode;
|
mode: ViewBuilderMode;
|
||||||
@ -26,12 +20,46 @@ type ViewBuilderDisplayState = {
|
|||||||
view?: DataHubView;
|
view?: DataHubView;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TriangleIcon = styled(VscTriangleDown)<{ isOpen: boolean }>`
|
||||||
|
color: ${(props) => (props.isOpen ? props.theme.styles['primary-color'] : ANTD_GRAY_V2[10])};
|
||||||
|
`;
|
||||||
|
|
||||||
const DEFAULT_VIEW_BUILDER_DISPLAY_STATE = {
|
const DEFAULT_VIEW_BUILDER_DISPLAY_STATE = {
|
||||||
mode: ViewBuilderMode.EDITOR,
|
mode: ViewBuilderMode.EDITOR,
|
||||||
visible: false,
|
visible: false,
|
||||||
view: undefined,
|
view: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ViewSelectContainer = styled.div`
|
||||||
|
&&& {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
.ant-select-selection-search {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
&.ant-select-open {
|
||||||
|
.ant-select-selection-placeholder,
|
||||||
|
.ant-select-selection-item {
|
||||||
|
color: ${(props) => props.theme.styles['primary-color']};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:not(.ant-select-open) {
|
||||||
|
.ant-select-selection-placeholder,
|
||||||
|
.ant-select-selection-item {
|
||||||
|
color: ${ANTD_GRAY_V2[10]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-select-selection-placeholder,
|
||||||
|
.ant-select-selection-item {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The View Select component allows you to select a View to apply to query on the current page. For example,
|
* The View Select component allows you to select a View to apply to query on the current page. For example,
|
||||||
* search, recommendations, and browse.
|
* search, recommendations, and browse.
|
||||||
@ -44,6 +72,7 @@ const DEFAULT_VIEW_BUILDER_DISPLAY_STATE = {
|
|||||||
export const ViewSelect = () => {
|
export const ViewSelect = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const userContext = useUserContext();
|
const userContext = useUserContext();
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [viewBuilderDisplayState, setViewBuilderDisplayState] = useState<ViewBuilderDisplayState>(
|
const [viewBuilderDisplayState, setViewBuilderDisplayState] = useState<ViewBuilderDisplayState>(
|
||||||
DEFAULT_VIEW_BUILDER_DISPLAY_STATE,
|
DEFAULT_VIEW_BUILDER_DISPLAY_STATE,
|
||||||
);
|
);
|
||||||
@ -153,54 +182,62 @@ export const ViewSelect = () => {
|
|||||||
(publicViews.filter((view) => view.urn === selectedUrn)?.length || 0) > 0 ||
|
(publicViews.filter((view) => view.urn === selectedUrn)?.length || 0) > 0 ||
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
const handleDropdownVisibleChange = (isNowOpen: boolean) => {
|
||||||
|
setIsOpen(isNowOpen);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ViewSelectContainer>
|
||||||
<ViewSelectToolTip visible={selectedUrn === undefined}>
|
<Select
|
||||||
<Select
|
data-testid="view-select"
|
||||||
data-testid="view-select"
|
style={{ minWidth: '120px', maxWidth: '200px' }}
|
||||||
style={selectStyle}
|
onChange={() => (selectRef?.current as any)?.blur()}
|
||||||
onChange={() => (selectRef?.current as any)?.blur()}
|
value={(foundSelectedUrn && selectedUrn) || undefined}
|
||||||
value={(foundSelectedUrn && selectedUrn) || undefined}
|
placeholder="All Entities"
|
||||||
placeholder="Select a View"
|
onSelect={onSelectView}
|
||||||
onSelect={onSelectView}
|
onClear={onClear}
|
||||||
onClear={onClear}
|
ref={selectRef}
|
||||||
allowClear
|
optionLabelProp="label"
|
||||||
ref={selectRef}
|
bordered={false}
|
||||||
optionLabelProp="label"
|
dropdownMatchSelectWidth={false}
|
||||||
dropdownStyle={dropdownStyle}
|
suffixIcon={<TriangleIcon isOpen={isOpen} />}
|
||||||
dropdownRender={(menu) => (
|
dropdownStyle={{
|
||||||
<ViewSelectDropdown
|
position: 'fixed',
|
||||||
menu={menu}
|
paddingBottom: 0,
|
||||||
hasViews={hasViews}
|
}}
|
||||||
onClickCreateView={onClickCreateView}
|
onDropdownVisibleChange={handleDropdownVisibleChange}
|
||||||
onClickClear={onClear}
|
dropdownRender={(menu) => (
|
||||||
onClickManageViews={onClickManageViews}
|
<ViewSelectDropdown
|
||||||
/>
|
menu={menu}
|
||||||
)}
|
hasViews={hasViews}
|
||||||
>
|
onClickCreateView={onClickCreateView}
|
||||||
{privateViewCount > 0 &&
|
onClickClear={onClear}
|
||||||
renderViewOptionGroup({
|
onClickManageViews={onClickManageViews}
|
||||||
views: privateViews,
|
/>
|
||||||
label: 'Private',
|
)}
|
||||||
isOwnedByUser: true,
|
>
|
||||||
userContext,
|
{privateViewCount > 0 &&
|
||||||
hoverViewUrn,
|
renderViewOptionGroup({
|
||||||
setHoverViewUrn,
|
views: privateViews,
|
||||||
onClickEditView,
|
label: 'Private',
|
||||||
onClickPreviewView,
|
isOwnedByUser: true,
|
||||||
})}
|
userContext,
|
||||||
{publicViewCount > 0 &&
|
hoverViewUrn,
|
||||||
renderViewOptionGroup({
|
setHoverViewUrn,
|
||||||
views: publicViews,
|
onClickEditView,
|
||||||
label: 'Public',
|
onClickPreviewView,
|
||||||
userContext,
|
})}
|
||||||
hoverViewUrn,
|
{publicViewCount > 0 &&
|
||||||
setHoverViewUrn,
|
renderViewOptionGroup({
|
||||||
onClickEditView,
|
views: publicViews,
|
||||||
onClickPreviewView,
|
label: 'Public',
|
||||||
})}
|
userContext,
|
||||||
</Select>
|
hoverViewUrn,
|
||||||
</ViewSelectToolTip>
|
setHoverViewUrn,
|
||||||
|
onClickEditView,
|
||||||
|
onClickPreviewView,
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
{viewBuilderDisplayState.visible && (
|
{viewBuilderDisplayState.visible && (
|
||||||
<ViewBuilder
|
<ViewBuilder
|
||||||
urn={viewBuilderDisplayState.view?.urn || undefined}
|
urn={viewBuilderDisplayState.view?.urn || undefined}
|
||||||
@ -210,6 +247,6 @@ export const ViewSelect = () => {
|
|||||||
onCancel={onCloseViewBuilder}
|
onCancel={onCloseViewBuilder}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</ViewSelectContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,9 +13,13 @@ type Props = {
|
|||||||
export const ViewSelectDropdown = ({ menu, hasViews, onClickCreateView, onClickManageViews, onClickClear }: Props) => {
|
export const ViewSelectDropdown = ({ menu, hasViews, onClickCreateView, onClickManageViews, onClickClear }: Props) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewSelectHeader onClickCreateView={onClickCreateView} onClickClear={onClickClear} />
|
<ViewSelectHeader onClickClear={onClickClear} />
|
||||||
{hasViews && menu}
|
{hasViews && menu}
|
||||||
<ViewSelectFooter onClickManageViews={onClickManageViews} />
|
<ViewSelectFooter
|
||||||
|
hasViews={hasViews}
|
||||||
|
onClickCreateView={onClickCreateView}
|
||||||
|
onClickManageViews={onClickManageViews}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,33 +1,47 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Button } from 'antd';
|
import { Button, Typography } from 'antd';
|
||||||
import { RightOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { ANTD_GRAY } from '../../shared/constants';
|
import { ANTD_GRAY, ANTD_GRAY_V2 } from '../../shared/constants';
|
||||||
|
import { NoMarginButton } from './styledComponents';
|
||||||
|
|
||||||
const ButtonContainer = styled.div`
|
const ButtonContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
color: ${ANTD_GRAY[6]};
|
color: ${ANTD_GRAY[6]};
|
||||||
width: 100%;
|
flex-direction: column;
|
||||||
justify-content: left;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledRightOutlined = styled(RightOutlined)`
|
const CreateViewButton = styled(NoMarginButton)<{ bordered: boolean }>`
|
||||||
&& {
|
&&& {
|
||||||
font-size: 8px;
|
text-align: left;
|
||||||
color: ${ANTD_GRAY[7]};
|
font-weight: normal;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
padding-left: 0px;
|
||||||
|
|
||||||
|
${(props) => props.bordered && `border-top: 1px solid ${ANTD_GRAY_V2[5]};`}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ManageViewsButton = styled(Button)`
|
const ManageViewsButton = styled(Button)`
|
||||||
font-weight: normal;
|
&&& {
|
||||||
color: ${ANTD_GRAY[7]};
|
color: ${(props) => props.theme.styles['primary-color']};
|
||||||
|
border-top: 1px solid ${ANTD_GRAY_V2[5]};
|
||||||
|
background-color: ${ANTD_GRAY_V2[2]};
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
hasViews: boolean;
|
||||||
|
onClickCreateView: () => void;
|
||||||
onClickManageViews: () => void;
|
onClickManageViews: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewSelectFooter = ({ onClickManageViews }: Props) => {
|
export const ViewSelectFooter = ({ hasViews, onClickCreateView, onClickManageViews }: Props) => {
|
||||||
const manageViewsButtonRef = useRef(null);
|
const manageViewsButtonRef = useRef(null);
|
||||||
|
|
||||||
const onHandleClickManageViews = () => {
|
const onHandleClickManageViews = () => {
|
||||||
@ -37,9 +51,17 @@ export const ViewSelectFooter = ({ onClickManageViews }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
|
<CreateViewButton
|
||||||
|
data-testid="view-select-create"
|
||||||
|
type="text"
|
||||||
|
bordered={hasViews}
|
||||||
|
onClick={onClickCreateView}
|
||||||
|
>
|
||||||
|
<PlusOutlined />
|
||||||
|
<Typography.Text>Create new view</Typography.Text>
|
||||||
|
</CreateViewButton>
|
||||||
<ManageViewsButton type="text" ref={manageViewsButtonRef} onClick={onHandleClickManageViews}>
|
<ManageViewsButton type="text" ref={manageViewsButtonRef} onClick={onHandleClickManageViews}>
|
||||||
Manage Views
|
Manage Views
|
||||||
<StyledRightOutlined />
|
|
||||||
</ManageViewsButton>
|
</ManageViewsButton>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,25 +1,32 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Button, Typography } from 'antd';
|
import { NoMarginButton } from './styledComponents';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { ANTD_GRAY_V2 } from '../../shared/constants';
|
||||||
|
|
||||||
const ButtonContainer = styled.div`
|
const ButtonContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const NoMarginButton = styled(Button)`
|
const AllEntitiesButton = styled(NoMarginButton)`
|
||||||
&& {
|
&&& {
|
||||||
margin: 0px;
|
font-weight: normal;
|
||||||
|
border-bottom: 1px solid ${ANTD_GRAY_V2[5]};
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
padding-left: 0px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClickCreateView: () => void;
|
|
||||||
onClickClear: () => void;
|
onClickClear: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewSelectHeader = ({ onClickCreateView, onClickClear }: Props) => {
|
export const ViewSelectHeader = ({ onClickClear }: Props) => {
|
||||||
const clearButtonRef = useRef(null);
|
const clearButtonRef = useRef(null);
|
||||||
|
|
||||||
const onHandleClickClear = () => {
|
const onHandleClickClear = () => {
|
||||||
@ -29,18 +36,14 @@ export const ViewSelectHeader = ({ onClickCreateView, onClickClear }: Props) =>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<NoMarginButton data-testid="view-select-create" type="text" onClick={onClickCreateView}>
|
<AllEntitiesButton
|
||||||
<PlusOutlined />
|
|
||||||
<Typography.Text strong> Create View</Typography.Text>
|
|
||||||
</NoMarginButton>
|
|
||||||
<NoMarginButton
|
|
||||||
data-testid="view-select-clear"
|
data-testid="view-select-clear"
|
||||||
type="link"
|
type="text"
|
||||||
ref={clearButtonRef}
|
ref={clearButtonRef}
|
||||||
onClick={onHandleClickClear}
|
onClick={onHandleClickClear}
|
||||||
>
|
>
|
||||||
Clear
|
All Entities
|
||||||
</NoMarginButton>
|
</AllEntitiesButton>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Tooltip } from 'antd';
|
|
||||||
|
|
||||||
const HeaderText = styled.div`
|
|
||||||
margin-bottom: 8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
children: React.ReactNode;
|
|
||||||
visible?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ViewSelectToolTip = ({ children, visible = true }: Props) => {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
overlayStyle={{ display: !visible ? 'none' : undefined }}
|
|
||||||
placement="right"
|
|
||||||
title={
|
|
||||||
<>
|
|
||||||
<HeaderText>Select a View to apply to search results.</HeaderText>
|
|
||||||
<div>Views help narrow down search results to those that matter most to you.</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { RightOutlined } from '@ant-design/icons';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { ANTD_GRAY } from '../../shared/constants';
|
||||||
|
|
||||||
|
export const NoMarginButton = styled(Button)`
|
||||||
|
&& {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const StyledRightOutlined = styled(RightOutlined)`
|
||||||
|
&& {
|
||||||
|
font-size: 8px;
|
||||||
|
color: ${ANTD_GRAY[7]};
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -152,6 +152,7 @@ export const HomePageHeader = () => {
|
|||||||
const showAcrylInfo = useIsShowAcrylInfoEnabled();
|
const showAcrylInfo = useIsShowAcrylInfoEnabled();
|
||||||
const { user } = userContext;
|
const { user } = userContext;
|
||||||
const viewUrn = userContext.localState?.selectedViewUrn;
|
const viewUrn = userContext.localState?.selectedViewUrn;
|
||||||
|
const viewsEnabled = appConfig.config?.viewsConfig?.enabled || false;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (suggestionsData !== undefined) {
|
if (suggestionsData !== undefined) {
|
||||||
@ -271,6 +272,7 @@ export const HomePageHeader = () => {
|
|||||||
onQueryChange={onAutoComplete}
|
onQueryChange={onAutoComplete}
|
||||||
autoCompleteStyle={styles.searchBox}
|
autoCompleteStyle={styles.searchBox}
|
||||||
entityRegistry={entityRegistry}
|
entityRegistry={entityRegistry}
|
||||||
|
viewsEnabled={viewsEnabled}
|
||||||
showQuickFilters
|
showQuickFilters
|
||||||
/>
|
/>
|
||||||
{searchResultsToShow && searchResultsToShow.length > 0 && (
|
{searchResultsToShow && searchResultsToShow.length > 0 && (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useMemo, useState, useRef, useCallback } from 'react';
|
import React, { useEffect, useMemo, useState, useRef, useCallback, EventHandler, SyntheticEvent } from 'react';
|
||||||
import { Input, AutoComplete, Button } from 'antd';
|
import { Input, AutoComplete, Button } from 'antd';
|
||||||
import { CloseCircleFilled, SearchOutlined } from '@ant-design/icons';
|
import { CloseCircleFilled, SearchOutlined } from '@ant-design/icons';
|
||||||
import styled from 'styled-components/macro';
|
import styled from 'styled-components/macro';
|
||||||
@ -6,7 +6,7 @@ import { useHistory } from 'react-router';
|
|||||||
import { AutoCompleteResultForEntity, Entity, EntityType, FacetFilterInput, ScenarioType } from '../../types.generated';
|
import { AutoCompleteResultForEntity, Entity, EntityType, FacetFilterInput, ScenarioType } from '../../types.generated';
|
||||||
import EntityRegistry from '../entity/EntityRegistry';
|
import EntityRegistry from '../entity/EntityRegistry';
|
||||||
import filterSearchQuery from './utils/filterSearchQuery';
|
import filterSearchQuery from './utils/filterSearchQuery';
|
||||||
import { ANTD_GRAY } from '../entity/shared/constants';
|
import { ANTD_GRAY, ANTD_GRAY_V2 } from '../entity/shared/constants';
|
||||||
import { getEntityPath } from '../entity/shared/containers/profile/utils';
|
import { getEntityPath } from '../entity/shared/containers/profile/utils';
|
||||||
import { EXACT_SEARCH_PREFIX } from './utils/constants';
|
import { EXACT_SEARCH_PREFIX } from './utils/constants';
|
||||||
import { useListRecommendationsQuery } from '../../graphql/recommendations.generated';
|
import { useListRecommendationsQuery } from '../../graphql/recommendations.generated';
|
||||||
@ -22,6 +22,7 @@ import { useUserContext } from '../context/useUserContext';
|
|||||||
import { navigateToSearchUrl } from './utils/navigateToSearchUrl';
|
import { navigateToSearchUrl } from './utils/navigateToSearchUrl';
|
||||||
import { getQuickFilterDetails } from './autoComplete/quickFilters/utils';
|
import { getQuickFilterDetails } from './autoComplete/quickFilters/utils';
|
||||||
import ViewAllSearchItem from './ViewAllSearchItem';
|
import ViewAllSearchItem from './ViewAllSearchItem';
|
||||||
|
import { ViewSelect } from '../entity/view/select/ViewSelect';
|
||||||
|
|
||||||
const StyledAutoComplete = styled(AutoComplete)`
|
const StyledAutoComplete = styled(AutoComplete)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -39,9 +40,14 @@ const StyledSearchBar = styled(Input)`
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: ${ANTD_GRAY[7]};
|
color: ${ANTD_GRAY[7]};
|
||||||
|
background-color: ${ANTD_GRAY_V2[2]};
|
||||||
}
|
}
|
||||||
> .ant-input {
|
> .ant-input {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
background-color: ${ANTD_GRAY_V2[2]};
|
||||||
|
}
|
||||||
|
> .ant-input::placeholder {
|
||||||
|
color: ${ANTD_GRAY_V2[10]};
|
||||||
}
|
}
|
||||||
.ant-input-clear-icon {
|
.ant-input-clear-icon {
|
||||||
height: 15px;
|
height: 15px;
|
||||||
@ -56,6 +62,16 @@ const ClearIcon = styled(CloseCircleFilled)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ViewSelectContainer = styled.div`
|
||||||
|
&&& {
|
||||||
|
border-right: 1px solid ${ANTD_GRAY_V2[5]};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SearchIcon = styled(SearchOutlined)`
|
||||||
|
color: ${ANTD_GRAY_V2[8]};
|
||||||
|
`;
|
||||||
|
|
||||||
const EXACT_AUTOCOMPLETE_OPTION_TYPE = 'exact_query';
|
const EXACT_AUTOCOMPLETE_OPTION_TYPE = 'exact_query';
|
||||||
const RECOMMENDED_QUERY_OPTION_TYPE = 'recommendation';
|
const RECOMMENDED_QUERY_OPTION_TYPE = 'recommendation';
|
||||||
|
|
||||||
@ -89,6 +105,10 @@ const renderRecommendedQuery = (query: string) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleStopPropagation: EventHandler<SyntheticEvent> = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
initialQuery?: string;
|
initialQuery?: string;
|
||||||
placeholderText: string;
|
placeholderText: string;
|
||||||
@ -102,6 +122,7 @@ interface Props {
|
|||||||
fixAutoComplete?: boolean;
|
fixAutoComplete?: boolean;
|
||||||
hideRecommendations?: boolean;
|
hideRecommendations?: boolean;
|
||||||
showQuickFilters?: boolean;
|
showQuickFilters?: boolean;
|
||||||
|
viewsEnabled?: boolean;
|
||||||
setIsSearchBarFocused?: (isSearchBarFocused: boolean) => void;
|
setIsSearchBarFocused?: (isSearchBarFocused: boolean) => void;
|
||||||
onFocus?: () => void;
|
onFocus?: () => void;
|
||||||
onBlur?: () => void;
|
onBlur?: () => void;
|
||||||
@ -127,6 +148,7 @@ export const SearchBar = ({
|
|||||||
fixAutoComplete,
|
fixAutoComplete,
|
||||||
hideRecommendations,
|
hideRecommendations,
|
||||||
showQuickFilters,
|
showQuickFilters,
|
||||||
|
viewsEnabled = false,
|
||||||
setIsSearchBarFocused,
|
setIsSearchBarFocused,
|
||||||
onFocus,
|
onFocus,
|
||||||
onBlur,
|
onBlur,
|
||||||
@ -326,6 +348,7 @@ export const SearchBar = ({
|
|||||||
listHeight={480}
|
listHeight={480}
|
||||||
>
|
>
|
||||||
<StyledSearchBar
|
<StyledSearchBar
|
||||||
|
bordered={false}
|
||||||
placeholder={placeholderText}
|
placeholder={placeholderText}
|
||||||
onPressEnter={() => {
|
onPressEnter={() => {
|
||||||
handleSearch(
|
handleSearch(
|
||||||
@ -334,7 +357,7 @@ export const SearchBar = ({
|
|||||||
getFiltersWithQuickFilter(selectedQuickFilter),
|
getFiltersWithQuickFilter(selectedQuickFilter),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
style={inputStyle}
|
style={{ ...inputStyle, color: 'red' }}
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
data-testid="search-input"
|
data-testid="search-input"
|
||||||
@ -342,15 +365,28 @@ export const SearchBar = ({
|
|||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
allowClear={{ clearIcon: <ClearIcon /> }}
|
allowClear={{ clearIcon: <ClearIcon /> }}
|
||||||
prefix={
|
prefix={
|
||||||
<SearchOutlined
|
<>
|
||||||
onClick={() => {
|
{viewsEnabled && (
|
||||||
handleSearch(
|
<ViewSelectContainer
|
||||||
filterSearchQuery(searchQuery || ''),
|
onClick={handleStopPropagation}
|
||||||
undefined,
|
onFocus={handleStopPropagation}
|
||||||
getFiltersWithQuickFilter(selectedQuickFilter),
|
onMouseDown={handleStopPropagation}
|
||||||
);
|
onKeyUp={handleStopPropagation}
|
||||||
}}
|
onKeyDown={handleStopPropagation}
|
||||||
/>
|
>
|
||||||
|
<ViewSelect />
|
||||||
|
</ViewSelectContainer>
|
||||||
|
)}
|
||||||
|
<SearchIcon
|
||||||
|
onClick={() => {
|
||||||
|
handleSearch(
|
||||||
|
filterSearchQuery(searchQuery || ''),
|
||||||
|
undefined,
|
||||||
|
getFiltersWithQuickFilter(selectedQuickFilter),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</StyledAutoComplete>
|
</StyledAutoComplete>
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import { ANTD_GRAY } from '../entity/shared/constants';
|
|||||||
import { HeaderLinks } from '../shared/admin/HeaderLinks';
|
import { HeaderLinks } from '../shared/admin/HeaderLinks';
|
||||||
import { useAppConfig, useIsShowAcrylInfoEnabled } from '../useAppConfig';
|
import { useAppConfig, useIsShowAcrylInfoEnabled } from '../useAppConfig';
|
||||||
import { DEFAULT_APP_CONFIG } from '../../appConfigContext';
|
import { DEFAULT_APP_CONFIG } from '../../appConfigContext';
|
||||||
import { ViewSelect } from '../entity/view/select/ViewSelect';
|
|
||||||
import DemoButton from '../entity/shared/components/styled/DemoButton';
|
import DemoButton from '../entity/shared/components/styled/DemoButton';
|
||||||
|
|
||||||
const { Header } = Layout;
|
const { Header } = Layout;
|
||||||
@ -49,10 +48,6 @@ const NavGroup = styled.div`
|
|||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ViewSelectContainer = styled.span`
|
|
||||||
margin-right: 14px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
initialQuery: string;
|
initialQuery: string;
|
||||||
placeholderText: string;
|
placeholderText: string;
|
||||||
@ -85,7 +80,7 @@ export const SearchHeader = ({
|
|||||||
const themeConfig = useTheme();
|
const themeConfig = useTheme();
|
||||||
const showAcrylInfo = useIsShowAcrylInfoEnabled();
|
const showAcrylInfo = useIsShowAcrylInfoEnabled();
|
||||||
const appConfig = useAppConfig();
|
const appConfig = useAppConfig();
|
||||||
const viewsEnabled = appConfig.config?.viewsConfig?.enabled;
|
const viewsEnabled = appConfig.config?.viewsConfig?.enabled || false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header style={styles.header as any}>
|
<Header style={styles.header as any}>
|
||||||
@ -108,16 +103,12 @@ export const SearchHeader = ({
|
|||||||
onQueryChange={onQueryChange}
|
onQueryChange={onQueryChange}
|
||||||
entityRegistry={entityRegistry}
|
entityRegistry={entityRegistry}
|
||||||
setIsSearchBarFocused={setIsSearchBarFocused}
|
setIsSearchBarFocused={setIsSearchBarFocused}
|
||||||
|
viewsEnabled={viewsEnabled}
|
||||||
fixAutoComplete
|
fixAutoComplete
|
||||||
showQuickFilters
|
showQuickFilters
|
||||||
/>
|
/>
|
||||||
</LogoSearchContainer>
|
</LogoSearchContainer>
|
||||||
<NavGroup>
|
<NavGroup>
|
||||||
{viewsEnabled && (
|
|
||||||
<ViewSelectContainer>
|
|
||||||
<ViewSelect />
|
|
||||||
</ViewSelectContainer>
|
|
||||||
)}
|
|
||||||
<HeaderLinks areLinksHidden={isSearchBarFocused} />
|
<HeaderLinks areLinksHidden={isSearchBarFocused} />
|
||||||
<ManageAccount urn={authenticatedUserUrn} pictureLink={authenticatedUserPictureLink || ''} />
|
<ManageAccount urn={authenticatedUserUrn} pictureLink={authenticatedUserPictureLink || ''} />
|
||||||
{showAcrylInfo && <DemoButton />}
|
{showAcrylInfo && <DemoButton />}
|
||||||
|
|||||||
@ -1,86 +1,88 @@
|
|||||||
function openViewEditDropDownAndClickId(data_id) {
|
function openViewEditDropDownAndClickId(data_id) {
|
||||||
cy.openMultiSelect('view-select');
|
cy.openMultiSelect("view-select");
|
||||||
cy.get('[data-testid="view-select-item"]').first().trigger('mouseover')
|
cy.get('[data-testid="view-select-item"]').first().trigger("mouseover");
|
||||||
cy.get('[data-testid="views-table-dropdown"]').first().trigger('mouseover');
|
cy.get('[data-testid="views-table-dropdown"]').first().trigger("mouseover");
|
||||||
cy.clickOptionWithTestId(data_id);
|
cy.clickOptionWithTestId(data_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("view select", () => {
|
describe("view select", () => {
|
||||||
it("click view select, create view, clear view, make defaults, clear view", () => {
|
it("click view select, create view, clear view, make defaults, clear view", () => {
|
||||||
cy.login();
|
cy.login();
|
||||||
let randomNumber = Math.floor(Math.random() * 100000);
|
let randomNumber = Math.floor(Math.random() * 100000);
|
||||||
const viewName = `Test View ${randomNumber}`
|
const viewName = `Test View ${randomNumber}`;
|
||||||
const newViewName = `New View Name ${randomNumber}`
|
const newViewName = `New View Name ${randomNumber}`;
|
||||||
|
|
||||||
// Resize Observer Loop warning can be safely ignored - ref. https://github.com/cypress-io/cypress/issues/22113
|
|
||||||
const resizeObserverLoopErrRe = "ResizeObserver loop limit exceeded";
|
|
||||||
cy.on('uncaught:exception', (err) => {
|
|
||||||
if (err.message.includes(resizeObserverLoopErrRe)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.goToStarSearchList();
|
|
||||||
|
|
||||||
cy.log("Create a View from the select")
|
|
||||||
cy.multiSelect('view-select', 'Create View');
|
|
||||||
cy.waitTextVisible("Create new View")
|
|
||||||
|
|
||||||
cy.enterTextInTestId("view-name-input", viewName);
|
|
||||||
|
|
||||||
cy.log("Add Column Glossary Term Filter")
|
|
||||||
cy.clickOptionWithText("Add Filter");
|
|
||||||
cy.clickOptionWithText("Column Glossary Term");
|
|
||||||
|
|
||||||
cy.waitTextVisible("Add Glossary Terms");
|
|
||||||
cy.selectOptionInTagTermModal("CypressColumnInfoType");
|
|
||||||
|
|
||||||
cy.clickOptionWithText("Save");
|
|
||||||
cy.ensureTextNotPresent("Save");
|
|
||||||
cy.waitTextVisible(viewName);
|
|
||||||
|
|
||||||
cy.log("Ensure the View filter has been applied correctly.")
|
|
||||||
cy.contains("SampleCypressHdfsDataset");
|
|
||||||
cy.contains("cypress_logging_events");
|
|
||||||
cy.contains("of 2 results");
|
|
||||||
|
|
||||||
cy.log("Clear the selected view")
|
|
||||||
cy.clearView(viewName);
|
|
||||||
cy.ensureTextNotPresent("of 2 results");
|
|
||||||
|
|
||||||
cy.log("Now edit the view")
|
|
||||||
openViewEditDropDownAndClickId('view-dropdown-edit');
|
|
||||||
cy.get(".ant-input-affix-wrapper > input[type='text']").first().clear().type(newViewName);
|
|
||||||
cy.log("Update the actual filters by adding another filter")
|
|
||||||
cy.contains("Add Filter").click();
|
|
||||||
cy.clickOptionWithTestId('adv-search-add-filter-description');
|
|
||||||
cy.enterTextInTestId('edit-text-input', "log event");
|
|
||||||
cy.clickOptionWithTestId('edit-text-done-btn');
|
|
||||||
|
|
||||||
cy.log("Save View")
|
|
||||||
cy.clickOptionWithTestId("view-builder-save");
|
|
||||||
|
|
||||||
cy.contains("cypress_logging_events");
|
|
||||||
cy.contains("of 1 result");
|
|
||||||
|
|
||||||
cy.log("Now set the View as the personal Default")
|
|
||||||
cy.clearView(viewName);
|
|
||||||
openViewEditDropDownAndClickId('view-dropdown-set-user-default');
|
|
||||||
|
|
||||||
cy.contains("of 1 result");
|
|
||||||
cy.clearView(newViewName);
|
|
||||||
cy.log("Now unset as the personal default")
|
|
||||||
openViewEditDropDownAndClickId('view-dropdown-remove-user-default');
|
|
||||||
|
|
||||||
cy.log("Now delete the View")
|
|
||||||
cy.clearView(newViewName);
|
|
||||||
openViewEditDropDownAndClickId('view-dropdown-delete');
|
|
||||||
cy.clickOptionWithText("Yes");
|
|
||||||
|
|
||||||
cy.log("Ensure that the view was deleted.")
|
|
||||||
cy.goToViewsSettings();
|
|
||||||
cy.ensureTextNotPresent(newViewName);
|
|
||||||
cy.ensureTextNotPresent("of 1 result");
|
|
||||||
|
|
||||||
|
// Resize Observer Loop warning can be safely ignored - ref. https://github.com/cypress-io/cypress/issues/22113
|
||||||
|
const resizeObserverLoopErrRe = "ResizeObserver loop limit exceeded";
|
||||||
|
cy.on("uncaught:exception", (err) => {
|
||||||
|
if (err.message.includes(resizeObserverLoopErrRe)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cy.goToStarSearchList();
|
||||||
|
|
||||||
|
cy.log("Create a View from the select");
|
||||||
|
cy.multiSelect("view-select", "Create new view");
|
||||||
|
cy.waitTextVisible("Create new view");
|
||||||
|
|
||||||
|
cy.enterTextInTestId("view-name-input", viewName);
|
||||||
|
|
||||||
|
cy.log("Add Column Glossary Term Filter");
|
||||||
|
cy.clickOptionWithText("Add Filter");
|
||||||
|
cy.clickOptionWithText("Column Glossary Term");
|
||||||
|
|
||||||
|
cy.waitTextVisible("Add Glossary Terms");
|
||||||
|
cy.selectOptionInTagTermModal("CypressColumnInfoType");
|
||||||
|
|
||||||
|
cy.clickOptionWithText("Save");
|
||||||
|
cy.ensureTextNotPresent("Save");
|
||||||
|
cy.waitTextVisible(viewName);
|
||||||
|
|
||||||
|
cy.log("Ensure the View filter has been applied correctly.");
|
||||||
|
cy.contains("SampleCypressHdfsDataset");
|
||||||
|
cy.contains("cypress_logging_events");
|
||||||
|
cy.contains("of 2 results");
|
||||||
|
|
||||||
|
cy.log("Clear the selected view");
|
||||||
|
cy.clearView(viewName);
|
||||||
|
cy.ensureTextNotPresent("of 2 results");
|
||||||
|
|
||||||
|
cy.log("Now edit the view");
|
||||||
|
openViewEditDropDownAndClickId("view-dropdown-edit");
|
||||||
|
cy.get(".ant-input-affix-wrapper > input[type='text']")
|
||||||
|
.first()
|
||||||
|
.clear()
|
||||||
|
.type(newViewName);
|
||||||
|
cy.log("Update the actual filters by adding another filter");
|
||||||
|
cy.contains("Add Filter").click();
|
||||||
|
cy.clickOptionWithTestId("adv-search-add-filter-description");
|
||||||
|
cy.enterTextInTestId("edit-text-input", "log event");
|
||||||
|
cy.clickOptionWithTestId("edit-text-done-btn");
|
||||||
|
|
||||||
|
cy.log("Save View");
|
||||||
|
cy.clickOptionWithTestId("view-builder-save");
|
||||||
|
|
||||||
|
cy.contains("cypress_logging_events");
|
||||||
|
cy.contains("of 1 result");
|
||||||
|
|
||||||
|
cy.log("Now set the View as the personal Default");
|
||||||
|
cy.clearView(viewName);
|
||||||
|
openViewEditDropDownAndClickId("view-dropdown-set-user-default");
|
||||||
|
|
||||||
|
cy.contains("of 1 result");
|
||||||
|
cy.clearView(newViewName);
|
||||||
|
cy.log("Now unset as the personal default");
|
||||||
|
openViewEditDropDownAndClickId("view-dropdown-remove-user-default");
|
||||||
|
|
||||||
|
cy.log("Now delete the View");
|
||||||
|
cy.clearView(newViewName);
|
||||||
|
openViewEditDropDownAndClickId("view-dropdown-delete");
|
||||||
|
cy.clickOptionWithText("Yes");
|
||||||
|
|
||||||
|
cy.log("Ensure that the view was deleted.");
|
||||||
|
cy.goToViewsSettings();
|
||||||
|
cy.ensureTextNotPresent(newViewName);
|
||||||
|
cy.ensureTextNotPresent("of 1 result");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user