feat(search): embedded view dropdown (#8598)

This commit is contained in:
Joshua Eilers 2023-08-09 09:30:48 -07:00 committed by GitHub
parent d3fd025b11
commit 0d03f4b7bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 323 additions and 220 deletions

View File

@ -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',

View File

@ -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]}
> >

View File

@ -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>
); );

View File

@ -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>
); );
}; };

View File

@ -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}
/>
</> </>
); );
}; };

View File

@ -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>
); );

View File

@ -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>
); );
}; };

View File

@ -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>
);
};

View File

@ -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]};
}
`;

View File

@ -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 && (

View File

@ -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>

View File

@ -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 />}

View File

@ -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");
});
}); });