mirror of
https://github.com/datahub-project/datahub.git
synced 2025-07-23 17:39:59 +00:00
refactor(ui): Adding checkbox option to select multiple results at once. (#5422)
This commit is contained in:
parent
91ca1e6425
commit
f59b21e951
@ -4,6 +4,7 @@ import { useHistory, useLocation, useParams } from 'react-router';
|
||||
import { message } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import type { CheckboxValueType } from 'antd/es/checkbox/Group';
|
||||
|
||||
import { useEntityRegistry } from '../../../../../useEntityRegistry';
|
||||
import { EntityType, FacetFilterInput } from '../../../../../../types.generated';
|
||||
@ -97,6 +98,9 @@ export const EmbeddedListSearch = ({
|
||||
|
||||
const [showFilters, setShowFilters] = useState(defaultShowFilters || false);
|
||||
|
||||
const [showSelectMode, setShowSelectMode] = useState(false);
|
||||
const [checkedSearchResults, setCheckedSearchResults] = useState<CheckboxValueType[]>([]);
|
||||
|
||||
const { refetch } = useGetSearchResults({
|
||||
variables: {
|
||||
input: {
|
||||
@ -184,11 +188,13 @@ export const EmbeddedListSearch = ({
|
||||
onSearch={onSearch}
|
||||
placeholderText={placeholderText}
|
||||
onToggleFilters={toggleFilters}
|
||||
showDownloadCsvButton
|
||||
callSearchOnVariables={callSearchOnVariables}
|
||||
entityFilters={entityFilters}
|
||||
filters={finalFilters}
|
||||
query={query}
|
||||
showSelectMode={showSelectMode}
|
||||
setShowSelectMode={setShowSelectMode}
|
||||
checkedSearchResults={checkedSearchResults}
|
||||
/>
|
||||
<EmbeddedListSearchResults
|
||||
loading={loading}
|
||||
@ -199,6 +205,9 @@ export const EmbeddedListSearch = ({
|
||||
onChangePage={onChangePage}
|
||||
page={page}
|
||||
showFilters={showFilters}
|
||||
showSelectMode={showSelectMode}
|
||||
setCheckedSearchResults={setCheckedSearchResults}
|
||||
checkedSearchResults={checkedSearchResults}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Button, Typography } from 'antd';
|
||||
import { FilterOutlined } from '@ant-design/icons';
|
||||
import { CloseCircleOutlined, FilterOutlined } from '@ant-design/icons';
|
||||
import type { CheckboxValueType } from 'antd/es/checkbox/Group';
|
||||
import styled from 'styled-components';
|
||||
import TabToolbar from '../TabToolbar';
|
||||
import { SearchBar } from '../../../../../search/SearchBar';
|
||||
@ -8,6 +9,7 @@ import { useEntityRegistry } from '../../../../../useEntityRegistry';
|
||||
import { EntityType, FacetFilterInput, SearchAcrossEntitiesInput } from '../../../../../../types.generated';
|
||||
import { SearchResultsInterface } from './types';
|
||||
import SearchExtendedMenu from './SearchExtendedMenu';
|
||||
// import SearchActionMenu from './SearchActionMenu';
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
display: flex;
|
||||
@ -25,28 +27,38 @@ const SearchMenuContainer = styled.div`
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
const SelectedText = styled(Typography.Text)`
|
||||
width: 70px;
|
||||
top: 5px;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
onSearch: (q: string) => void;
|
||||
onToggleFilters: () => void;
|
||||
placeholderText?: string | null;
|
||||
showDownloadCsvButton?: boolean;
|
||||
callSearchOnVariables: (variables: {
|
||||
input: SearchAcrossEntitiesInput;
|
||||
}) => Promise<SearchResultsInterface | null | undefined>;
|
||||
entityFilters: EntityType[];
|
||||
filters: FacetFilterInput[];
|
||||
query: string;
|
||||
setShowSelectMode: (showSelectMode: boolean) => any;
|
||||
showSelectMode: boolean;
|
||||
checkedSearchResults: CheckboxValueType[];
|
||||
};
|
||||
|
||||
export default function EmbeddedListSearchHeader({
|
||||
onSearch,
|
||||
onToggleFilters,
|
||||
placeholderText,
|
||||
showDownloadCsvButton,
|
||||
callSearchOnVariables,
|
||||
entityFilters,
|
||||
filters,
|
||||
query,
|
||||
setShowSelectMode,
|
||||
showSelectMode,
|
||||
checkedSearchResults,
|
||||
}: Props) {
|
||||
const entityRegistry = useEntityRegistry();
|
||||
|
||||
@ -62,6 +74,7 @@ export default function EmbeddedListSearchHeader({
|
||||
<Typography.Text>Filters</Typography.Text>
|
||||
</Button>
|
||||
<SearchAndDownloadContainer>
|
||||
{showSelectMode && <SelectedText>{`${checkedSearchResults.length} selected`}</SelectedText>}
|
||||
<SearchBar
|
||||
initialQuery=""
|
||||
placeholderText={placeholderText || 'Search entities...'}
|
||||
@ -79,13 +92,29 @@ export default function EmbeddedListSearchHeader({
|
||||
entityRegistry={entityRegistry}
|
||||
/>
|
||||
{/* TODO: in the future, when we add more menu items, we'll show this always */}
|
||||
{showDownloadCsvButton && (
|
||||
{showSelectMode ? (
|
||||
<>
|
||||
<Button
|
||||
style={{
|
||||
marginLeft: '5px',
|
||||
}}
|
||||
onClick={() => setShowSelectMode(false)}
|
||||
type="text"
|
||||
>
|
||||
<CloseCircleOutlined /> Cancel
|
||||
</Button>
|
||||
{/* <SearchMenuContainer>
|
||||
<SearchActionMenu checkedSearchResults={checkedSearchResults} />
|
||||
</SearchMenuContainer> */}
|
||||
</>
|
||||
) : (
|
||||
<SearchMenuContainer>
|
||||
<SearchExtendedMenu
|
||||
callSearchOnVariables={callSearchOnVariables}
|
||||
entityFilters={entityFilters}
|
||||
filters={filters}
|
||||
query={query}
|
||||
setShowSelectMode={setShowSelectMode}
|
||||
/>
|
||||
</SearchMenuContainer>
|
||||
)}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Pagination, Typography } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
import type { CheckboxValueType } from 'antd/es/checkbox/Group';
|
||||
import { FacetFilterInput, FacetMetadata, SearchResults as SearchResultType } from '../../../../../../types.generated';
|
||||
import { SearchFilters } from '../../../../../search/SearchFilters';
|
||||
import { SearchCfg } from '../../../../../../conf';
|
||||
@ -94,6 +95,9 @@ interface Props {
|
||||
showFilters?: boolean;
|
||||
onChangeFilters: (filters: Array<FacetFilterInput>) => void;
|
||||
onChangePage: (page: number) => void;
|
||||
showSelectMode: boolean;
|
||||
setCheckedSearchResults: (checkedSearchResults: Array<CheckboxValueType>) => any;
|
||||
checkedSearchResults: CheckboxValueType[];
|
||||
}
|
||||
|
||||
export const EmbeddedListSearchResults = ({
|
||||
@ -105,6 +109,9 @@ export const EmbeddedListSearchResults = ({
|
||||
showFilters,
|
||||
onChangeFilters,
|
||||
onChangePage,
|
||||
showSelectMode,
|
||||
setCheckedSearchResults,
|
||||
checkedSearchResults,
|
||||
}: Props) => {
|
||||
const pageStart = searchResponse?.start || 0;
|
||||
const pageSize = searchResponse?.count || 0;
|
||||
@ -151,6 +158,9 @@ export const EmbeddedListSearchResults = ({
|
||||
degree: searchResult['degree'],
|
||||
})) || []
|
||||
}
|
||||
showSelectMode={showSelectMode}
|
||||
setCheckedSearchResults={setCheckedSearchResults}
|
||||
checkedSearchResults={checkedSearchResults}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { Button, Dropdown, Menu } from 'antd';
|
||||
import { MoreOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import styled from 'styled-components';
|
||||
import type { CheckboxValueType } from 'antd/es/checkbox/Group';
|
||||
|
||||
const MenuIcon = styled(MoreOutlined)`
|
||||
font-size: 15px;
|
||||
height: 20px;
|
||||
`;
|
||||
|
||||
const SelectButton = styled(Button)`
|
||||
font-size: 12px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
checkedSearchResults: CheckboxValueType[];
|
||||
};
|
||||
|
||||
// currently only contains Download As Csv but will be extended to contain other actions as well
|
||||
export default function SearchActionMenu({ checkedSearchResults }: Props) {
|
||||
console.log('checkedSearchResults:: ', checkedSearchResults);
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item key="0">
|
||||
<SelectButton type="text">
|
||||
<PlusOutlined />
|
||||
Add Tags
|
||||
</SelectButton>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="1">
|
||||
<SelectButton type="text">
|
||||
<PlusOutlined />
|
||||
Add Terms
|
||||
</SelectButton>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2">
|
||||
<SelectButton type="text">
|
||||
<PlusOutlined />
|
||||
Add Owners
|
||||
</SelectButton>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown overlay={menu} trigger={['click']}>
|
||||
<MenuIcon />
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Dropdown, Menu } from 'antd';
|
||||
import { MoreOutlined } from '@ant-design/icons';
|
||||
// import { Button, Dropdown, Menu } from 'antd';
|
||||
// import { MoreOutlined, SelectOutlined } from '@ant-design/icons';
|
||||
import styled from 'styled-components';
|
||||
import { EntityType, FacetFilterInput, SearchAcrossEntitiesInput } from '../../../../../../types.generated';
|
||||
import { SearchResultsInterface } from './types';
|
||||
@ -12,6 +14,12 @@ const MenuIcon = styled(MoreOutlined)`
|
||||
height: 20px;
|
||||
`;
|
||||
|
||||
// const SelectButton = styled(Button)`
|
||||
// font-size: 12px;
|
||||
// padding-left: 12px;
|
||||
// padding-right: 12px;
|
||||
// `;
|
||||
|
||||
type Props = {
|
||||
callSearchOnVariables: (variables: {
|
||||
input: SearchAcrossEntitiesInput;
|
||||
@ -19,13 +27,22 @@ type Props = {
|
||||
entityFilters: EntityType[];
|
||||
filters: FacetFilterInput[];
|
||||
query: string;
|
||||
setShowSelectMode?: (showSelectMode: boolean) => any;
|
||||
};
|
||||
|
||||
// currently only contains Download As Csv but will be extended to contain other actions as well
|
||||
export default function SearchExtendedMenu({ callSearchOnVariables, entityFilters, filters, query }: Props) {
|
||||
export default function SearchExtendedMenu({
|
||||
callSearchOnVariables,
|
||||
entityFilters,
|
||||
filters,
|
||||
query,
|
||||
setShowSelectMode,
|
||||
}: Props) {
|
||||
const [isDownloadingCsv, setIsDownloadingCsv] = useState(false);
|
||||
const [showDownloadAsCsvModal, setShowDownloadAsCsvModal] = useState(false);
|
||||
|
||||
// TO DO: Need to implement Select Mode
|
||||
console.log('setShowSelectMode:', setShowSelectMode);
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item key="0">
|
||||
@ -34,6 +51,14 @@ export default function SearchExtendedMenu({ callSearchOnVariables, entityFilter
|
||||
setShowDownloadAsCsvModal={setShowDownloadAsCsvModal}
|
||||
/>
|
||||
</Menu.Item>
|
||||
{/* <Menu.Item key="1">
|
||||
{setShowSelectMode && (
|
||||
<SelectButton type="text" onClick={() => setShowSelectMode(true)}>
|
||||
<SelectOutlined />
|
||||
Select...
|
||||
</SelectButton>
|
||||
)}
|
||||
</Menu.Item> */}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
|
@ -10,11 +10,11 @@ import {
|
||||
GlossaryTerms,
|
||||
SearchInsight,
|
||||
Container,
|
||||
Domain,
|
||||
ParentContainersResult,
|
||||
Maybe,
|
||||
CorpUser,
|
||||
Deprecation,
|
||||
Domain,
|
||||
} from '../../types.generated';
|
||||
import TagTermGroup from '../shared/tags/TagTermGroup';
|
||||
import { ANTD_GRAY } from '../entity/shared/constants';
|
||||
@ -176,7 +176,7 @@ interface Props {
|
||||
insights?: Array<SearchInsight> | null;
|
||||
glossaryTerms?: GlossaryTerms;
|
||||
container?: Container;
|
||||
domain?: Domain | null;
|
||||
domain?: Domain | undefined | null;
|
||||
entityCount?: number;
|
||||
dataTestID?: string;
|
||||
titleSizePx?: number;
|
||||
@ -239,8 +239,13 @@ export default function DefaultPreviewCard({
|
||||
|
||||
const { parentContainersRef, areContainersTruncated } = useParentContainersTruncation(container);
|
||||
|
||||
const onPreventMouseDown = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<PreviewContainer data-testid={dataTestID}>
|
||||
<PreviewContainer data-testid={dataTestID} onMouseDown={onPreventMouseDown}>
|
||||
<LeftColumn>
|
||||
<TitleContainer>
|
||||
<PlatformContentView
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Divider, List } from 'antd';
|
||||
import { Divider, List, Checkbox } from 'antd';
|
||||
import type { CheckboxValueType } from 'antd/es/checkbox/Group';
|
||||
import styled from 'styled-components';
|
||||
import { Entity } from '../../../../types.generated';
|
||||
import { useEntityRegistry } from '../../../useEntityRegistry';
|
||||
@ -53,6 +54,34 @@ type AdditionalProperties = {
|
||||
degree?: number;
|
||||
};
|
||||
|
||||
const CheckBoxGroup = styled(Checkbox.Group)`
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background-color: rgb(255, 255, 255);
|
||||
padding-right: 32px;
|
||||
padding-left: 32px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
> .ant-checkbox-group-item {
|
||||
display: block;
|
||||
margin-right: 0;
|
||||
}
|
||||
&&& .ant-checkbox {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 48px;
|
||||
}
|
||||
`;
|
||||
|
||||
const LabelContainer = styled.span`
|
||||
position: relative;
|
||||
left: 24px;
|
||||
bottom: 6px;
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
// additional data about the search result that is not part of the entity used to enrich the
|
||||
// presentation of the entity. For example, metadata about how the entity is related for the case
|
||||
@ -60,9 +89,19 @@ type Props = {
|
||||
additionalPropertiesList?: Array<AdditionalProperties>;
|
||||
entities: Array<Entity>;
|
||||
onClick?: (index: number) => void;
|
||||
showSelectMode?: boolean;
|
||||
setCheckedSearchResults?: (checkedSearchResults: Array<CheckboxValueType>) => any;
|
||||
checkedSearchResults?: CheckboxValueType[];
|
||||
};
|
||||
|
||||
export const EntityNameList = ({ additionalPropertiesList, entities, onClick }: Props) => {
|
||||
export const EntityNameList = ({
|
||||
additionalPropertiesList,
|
||||
entities,
|
||||
onClick,
|
||||
showSelectMode,
|
||||
setCheckedSearchResults,
|
||||
checkedSearchResults,
|
||||
}: Props) => {
|
||||
const entityRegistry = useEntityRegistry();
|
||||
if (
|
||||
additionalPropertiesList?.length !== undefined &&
|
||||
@ -74,7 +113,53 @@ export const EntityNameList = ({ additionalPropertiesList, entities, onClick }:
|
||||
{ additionalPropertiesList, entities },
|
||||
);
|
||||
}
|
||||
|
||||
const onChange = (checkedValues: CheckboxValueType[]) => {
|
||||
setCheckedSearchResults?.(checkedValues);
|
||||
};
|
||||
|
||||
const options = entities.map((entity, index) => {
|
||||
const additionalProperties = additionalPropertiesList?.[index];
|
||||
const genericProps = entityRegistry.getGenericEntityProperties(entity.type, entity);
|
||||
const platformLogoUrl = genericProps?.platform?.properties?.logoUrl;
|
||||
const platformName =
|
||||
genericProps?.platform?.properties?.displayName || capitalizeFirstLetter(genericProps?.platform?.name);
|
||||
const entityTypeName = entityRegistry.getEntityName(entity.type);
|
||||
const displayName = entityRegistry.getDisplayName(entity.type, entity);
|
||||
const url = entityRegistry.getEntityUrl(entity.type, entity.urn);
|
||||
const fallbackIcon = entityRegistry.getIcon(entity.type, 18, IconStyleType.ACCENT);
|
||||
const subType = genericProps?.subTypes?.typeNames?.length && genericProps?.subTypes?.typeNames[0];
|
||||
const entityCount = genericProps?.entityCount;
|
||||
return {
|
||||
label: (
|
||||
<LabelContainer>
|
||||
<DefaultPreviewCard
|
||||
name={displayName}
|
||||
logoUrl={platformLogoUrl || undefined}
|
||||
logoComponent={fallbackIcon}
|
||||
url={url}
|
||||
platform={platformName || undefined}
|
||||
type={subType || entityTypeName}
|
||||
titleSizePx={14}
|
||||
tags={genericProps?.globalTags || undefined}
|
||||
glossaryTerms={genericProps?.glossaryTerms || undefined}
|
||||
domain={genericProps?.domain?.domain}
|
||||
onClick={() => onClick?.(index)}
|
||||
entityCount={entityCount}
|
||||
degree={additionalProperties?.degree}
|
||||
/>
|
||||
<ThinDivider />
|
||||
</LabelContainer>
|
||||
),
|
||||
value: entity.urn,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{showSelectMode ? (
|
||||
<CheckBoxGroup options={options} onChange={onChange} value={checkedSearchResults} />
|
||||
) : (
|
||||
<StyledList
|
||||
bordered
|
||||
dataSource={entities}
|
||||
@ -89,7 +174,8 @@ export const EntityNameList = ({ additionalPropertiesList, entities, onClick }:
|
||||
const displayName = entityRegistry.getDisplayName(entity.type, entity);
|
||||
const url = entityRegistry.getEntityUrl(entity.type, entity.urn);
|
||||
const fallbackIcon = entityRegistry.getIcon(entity.type, 18, IconStyleType.ACCENT);
|
||||
const subType = genericProps?.subTypes?.typeNames?.length && genericProps?.subTypes?.typeNames[0];
|
||||
const subType =
|
||||
genericProps?.subTypes?.typeNames?.length && genericProps?.subTypes?.typeNames[0];
|
||||
const entityCount = genericProps?.entityCount;
|
||||
return (
|
||||
<>
|
||||
@ -115,5 +201,7 @@ export const EntityNameList = ({ additionalPropertiesList, entities, onClick }:
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ type Props = {
|
||||
editableTags?: GlobalTags | null;
|
||||
editableGlossaryTerms?: GlossaryTerms | null;
|
||||
uneditableGlossaryTerms?: GlossaryTerms | null;
|
||||
domain?: Domain | null;
|
||||
domain?: Domain | undefined | null;
|
||||
canRemove?: boolean;
|
||||
canAddTag?: boolean;
|
||||
canAddTerm?: boolean;
|
||||
|
Loading…
x
Reference in New Issue
Block a user