mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-01 21:27:15 +00:00
feat(homepage): Add support for Quick Link modules in the new home page (#14141)
Co-authored-by: Chris Collins <chriscollins3456@gmail.com>
This commit is contained in:
parent
5e98093e31
commit
0c74310e3e
@ -11,6 +11,7 @@ import com.linkedin.datahub.graphql.concurrency.GraphQLConcurrencyUtils;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.DataHubPageModule;
|
||||
import com.linkedin.datahub.graphql.generated.DataHubPageModuleType;
|
||||
import com.linkedin.datahub.graphql.generated.LinkModuleParamsInput;
|
||||
import com.linkedin.datahub.graphql.generated.PageModuleScope;
|
||||
import com.linkedin.datahub.graphql.generated.UpsertPageModuleInput;
|
||||
import com.linkedin.datahub.graphql.types.module.PageModuleMapper;
|
||||
@ -87,8 +88,18 @@ public class UpsertPageModuleResolver implements DataFetcher<CompletableFuture<D
|
||||
DataHubPageModuleParams gmsParams = new DataHubPageModuleParams();
|
||||
|
||||
if (paramsInput.getLinkParams() != null) {
|
||||
LinkModuleParamsInput inputValues = paramsInput.getLinkParams();
|
||||
com.linkedin.module.LinkModuleParams linkParams = new com.linkedin.module.LinkModuleParams();
|
||||
linkParams.setLinkUrn(UrnUtils.getUrn(paramsInput.getLinkParams().getLinkUrn()));
|
||||
|
||||
linkParams.setLinkUrl(inputValues.getLinkUrl());
|
||||
|
||||
if (inputValues.getImageUrl() != null) {
|
||||
linkParams.setImageUrl(inputValues.getImageUrl());
|
||||
}
|
||||
if (inputValues.getDescription() != null) {
|
||||
linkParams.setDescription(inputValues.getDescription());
|
||||
}
|
||||
|
||||
gmsParams.setLinkParams(linkParams);
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,7 @@ package com.linkedin.datahub.graphql.types.module;
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.generated.AssetCollectionModuleParams;
|
||||
import com.linkedin.datahub.graphql.generated.EntityType;
|
||||
import com.linkedin.datahub.graphql.generated.LinkModuleParams;
|
||||
import com.linkedin.datahub.graphql.generated.Post;
|
||||
import com.linkedin.datahub.graphql.generated.RichTextModuleParams;
|
||||
import com.linkedin.datahub.graphql.types.mappers.ModelMapper;
|
||||
import com.linkedin.module.DataHubPageModuleParams;
|
||||
@ -32,13 +30,22 @@ public class PageModuleParamsMapper
|
||||
new com.linkedin.datahub.graphql.generated.DataHubPageModuleParams();
|
||||
|
||||
// Map link params if present
|
||||
if (params.getLinkParams() != null && params.getLinkParams().getLinkUrn() != null) {
|
||||
LinkModuleParams linkModuleParams = new LinkModuleParams();
|
||||
Post link = new Post();
|
||||
link.setUrn(params.getLinkParams().getLinkUrn().toString());
|
||||
link.setType(EntityType.POST);
|
||||
linkModuleParams.setLink(link);
|
||||
result.setLinkParams(linkModuleParams);
|
||||
if (params.getLinkParams() != null) {
|
||||
com.linkedin.module.LinkModuleParams linkParams = params.getLinkParams();
|
||||
|
||||
if (linkParams.getLinkUrl() != null) {
|
||||
LinkModuleParams linkModuleParams = new LinkModuleParams();
|
||||
|
||||
linkModuleParams.setLinkUrl(linkParams.getLinkUrl());
|
||||
|
||||
if (linkParams.getImageUrl() != null) {
|
||||
linkModuleParams.setImageUrl(linkParams.getImageUrl());
|
||||
}
|
||||
if (linkParams.getDescription() != null) {
|
||||
linkModuleParams.setDescription(linkParams.getDescription());
|
||||
}
|
||||
result.setLinkParams(linkModuleParams);
|
||||
}
|
||||
}
|
||||
|
||||
// Map rich text params if present
|
||||
|
||||
@ -90,9 +90,19 @@ Input for the params required if the module is type LINK
|
||||
"""
|
||||
input LinkModuleParamsInput {
|
||||
"""
|
||||
The URN of the Post entity containing the link
|
||||
The URL of the link
|
||||
"""
|
||||
linkUrn: String!
|
||||
linkUrl: String!
|
||||
|
||||
"""
|
||||
The image URL of the link
|
||||
"""
|
||||
imageUrl: String
|
||||
|
||||
"""
|
||||
The description of the link
|
||||
"""
|
||||
description: String
|
||||
}
|
||||
|
||||
"""
|
||||
@ -229,9 +239,19 @@ The params required if the module is type LINK
|
||||
"""
|
||||
type LinkModuleParams {
|
||||
"""
|
||||
The Post entity containing the link
|
||||
The URL of the link
|
||||
"""
|
||||
link: Post!
|
||||
linkUrl: String!
|
||||
|
||||
"""
|
||||
The image URL of the link
|
||||
"""
|
||||
imageUrl: String
|
||||
|
||||
"""
|
||||
The description of the link
|
||||
"""
|
||||
description: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@ -138,7 +138,7 @@ public class UpsertPageModuleResolverTest {
|
||||
|
||||
PageModuleParamsInput paramsInput = new PageModuleParamsInput();
|
||||
paramsInput.setLinkParams(new LinkModuleParamsInput());
|
||||
paramsInput.getLinkParams().setLinkUrn("urn:li:post:test-post");
|
||||
paramsInput.getLinkParams().setLinkUrl("https://example.com/test-link");
|
||||
input.setParams(paramsInput);
|
||||
|
||||
Urn moduleUrn = UrnUtils.getUrn(TEST_MODULE_URN);
|
||||
@ -286,7 +286,7 @@ public class UpsertPageModuleResolverTest {
|
||||
// Set link params instead of rich text params
|
||||
PageModuleParamsInput paramsInput = new PageModuleParamsInput();
|
||||
paramsInput.setLinkParams(new LinkModuleParamsInput());
|
||||
paramsInput.getLinkParams().setLinkUrn("urn:li:post:test-post");
|
||||
paramsInput.getLinkParams().setLinkUrl("https://example.com/test-link");
|
||||
input.setParams(paramsInput);
|
||||
|
||||
when(mockEnvironment.getArgument("input")).thenReturn(input);
|
||||
|
||||
@ -202,7 +202,9 @@ public class PageModuleTypeTest {
|
||||
com.linkedin.module.DataHubPageModuleParams params =
|
||||
new com.linkedin.module.DataHubPageModuleParams();
|
||||
com.linkedin.module.LinkModuleParams linkParams = new com.linkedin.module.LinkModuleParams();
|
||||
linkParams.setLinkUrn(UrnUtils.getUrn("urn:li:post:test-post"));
|
||||
|
||||
linkParams.setLinkUrl("https://example.com/test-link");
|
||||
|
||||
params.setLinkParams(linkParams);
|
||||
gmsProperties.setParams(params);
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import YourAssetsModule from '@app/homeV3/modules/YourAssetsModule';
|
||||
import AssetCollectionModule from '@app/homeV3/modules/assetCollection/AssetCollectionModule';
|
||||
import DocumentationModule from '@app/homeV3/modules/documentation/DocumentationModule';
|
||||
import TopDomainsModule from '@app/homeV3/modules/domains/TopDomainsModule';
|
||||
import LinkModule from '@app/homeV3/modules/link/LinkModule';
|
||||
|
||||
import { DataHubPageModuleType } from '@types';
|
||||
|
||||
@ -17,6 +18,7 @@ function Module(props: ModuleProps) {
|
||||
if (module.properties.type === DataHubPageModuleType.OwnedAssets) return YourAssetsModule;
|
||||
if (module.properties.type === DataHubPageModuleType.Domains) return TopDomainsModule;
|
||||
if (module.properties.type === DataHubPageModuleType.AssetCollection) return AssetCollectionModule;
|
||||
if (module.properties.type === DataHubPageModuleType.Link) return LinkModule;
|
||||
if (module.properties.type === DataHubPageModuleType.RichText) return DocumentationModule;
|
||||
|
||||
// TODO: remove the sample large module once we have other modules to fill this out
|
||||
|
||||
@ -7,6 +7,7 @@ import ModuleContainer from '@app/homeV3/module/components/ModuleContainer';
|
||||
import ModuleMenu from '@app/homeV3/module/components/ModuleMenu';
|
||||
import ModuleName from '@app/homeV3/module/components/ModuleName';
|
||||
import { ModuleProps } from '@app/homeV3/module/types';
|
||||
import { FloatingRightHeaderSection } from '@app/homeV3/styledComponents';
|
||||
|
||||
const ModuleHeader = styled.div`
|
||||
position: relative;
|
||||
@ -33,18 +34,6 @@ const DragHandle = styled.div<{ $isDragging?: boolean }>`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const FloatingRightHeaderSection = styled.div`
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-right: 16px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const Content = styled.div<{ $hasViewAll: boolean }>`
|
||||
margin: 16px;
|
||||
overflow-y: auto;
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { borders, colors, radius } from '@components';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const ModuleContainer = styled.div<{ $height: string; $isDragging?: boolean }>`
|
||||
const ModuleContainer = styled.div<{ $height?: string; $isDragging?: boolean }>`
|
||||
background: ${colors.white};
|
||||
border: ${borders['1px']} ${colors.gray[100]};
|
||||
border-radius: ${radius.lg};
|
||||
flex: 1;
|
||||
overflow-x: hidden;
|
||||
|
||||
height: ${(props) => props.$height};
|
||||
box-shadow:
|
||||
0px 2px 18px 0px rgba(17, 7, 69, 0.01),
|
||||
0px 4px 12px 0px rgba(17, 7, 69, 0.03);
|
||||
@ -24,6 +22,12 @@ const ModuleContainer = styled.div<{ $height: string; $isDragging?: boolean }>`
|
||||
transform: translateZ(0) scale(1.02);
|
||||
opacity: 0.5;
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.$height &&
|
||||
`
|
||||
height: ${props.$height};
|
||||
`}
|
||||
`;
|
||||
|
||||
export default ModuleContainer;
|
||||
|
||||
@ -15,6 +15,8 @@ const StyledIcon = styled(Icon)`
|
||||
}
|
||||
` as typeof Icon;
|
||||
|
||||
const DropdownWrapper = styled.div``;
|
||||
|
||||
interface Props {
|
||||
module: PageModuleFragment;
|
||||
position: ModulePositionInput;
|
||||
@ -40,39 +42,45 @@ export default function ModuleMenu({ module, position }: Props) {
|
||||
});
|
||||
}, [removeModule, module.urn, position]);
|
||||
|
||||
const handleMenuClick = useCallback((e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
menu={{
|
||||
items: [
|
||||
...(canEdit
|
||||
? [
|
||||
{
|
||||
title: 'Edit',
|
||||
key: 'edit',
|
||||
label: 'Edit',
|
||||
style: {
|
||||
color: colors.gray[600],
|
||||
fontSize: '14px',
|
||||
<DropdownWrapper onClick={handleMenuClick}>
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
menu={{
|
||||
items: [
|
||||
...(canEdit
|
||||
? [
|
||||
{
|
||||
title: 'Edit',
|
||||
key: 'edit',
|
||||
label: 'Edit',
|
||||
style: {
|
||||
color: colors.gray[600],
|
||||
fontSize: '14px',
|
||||
},
|
||||
onClick: handleEditModule,
|
||||
},
|
||||
onClick: handleEditModule,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: 'Delete',
|
||||
label: 'Delete',
|
||||
key: 'delete',
|
||||
style: {
|
||||
color: colors.red[500],
|
||||
fontSize: '14px',
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: 'Delete',
|
||||
label: 'Delete',
|
||||
key: 'delete',
|
||||
style: {
|
||||
color: colors.red[500],
|
||||
fontSize: '14px',
|
||||
},
|
||||
onClick: handleDelete,
|
||||
},
|
||||
onClick: handleDelete,
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<StyledIcon icon="DotsThreeVertical" source="phosphor" size="lg" />
|
||||
</Dropdown>
|
||||
],
|
||||
}}
|
||||
>
|
||||
<StyledIcon icon="DotsThreeVertical" source="phosphor" size="lg" />
|
||||
</Dropdown>
|
||||
</DropdownWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import ModuleContainer from '@app/homeV3/module/components/ModuleContainer';
|
||||
import ModuleMenu from '@app/homeV3/module/components/ModuleMenu';
|
||||
import { ModuleProps } from '@app/homeV3/module/types';
|
||||
import { FloatingRightHeaderSection } from '@app/homeV3/styledComponents';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
margin: 16px 32px 16px 16px;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledModuleContainer = styled(ModuleContainer)<{ clickable?: boolean }>`
|
||||
max-height: 72px;
|
||||
|
||||
${({ clickable }) => clickable && `cursor: pointer;`}
|
||||
`;
|
||||
|
||||
export default function SmallModule({ children, module, position, onClick }: React.PropsWithChildren<ModuleProps>) {
|
||||
return (
|
||||
<StyledModuleContainer clickable={!!onClick} onClick={onClick}>
|
||||
<Container>
|
||||
<Content>{children}</Content>
|
||||
<FloatingRightHeaderSection>
|
||||
<ModuleMenu module={module} position={position} />
|
||||
</FloatingRightHeaderSection>
|
||||
</Container>
|
||||
</StyledModuleContainer>
|
||||
);
|
||||
}
|
||||
@ -14,6 +14,7 @@ vi.mock('styled-components', () => {
|
||||
};
|
||||
styledFactory.div = styledFactory('div');
|
||||
styledFactory.button = styledFactory('button');
|
||||
styledFactory.Icon = styledFactory(() => null);
|
||||
return {
|
||||
__esModule: true,
|
||||
default: styledFactory,
|
||||
@ -45,6 +46,7 @@ vi.mock('@components', () => ({
|
||||
),
|
||||
Loader: () => <div data-testid="loader">Loading...</div>,
|
||||
Text: ({ children }: any) => <span>{children}</span>,
|
||||
Icon: () => <svg />,
|
||||
borders: {
|
||||
'1px': '1px solid',
|
||||
},
|
||||
|
||||
@ -7,4 +7,5 @@ import { PageModuleFragment } from '@graphql/template.generated';
|
||||
export interface ModuleProps {
|
||||
module: PageModuleFragment;
|
||||
position: ModulePositionInput;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import React, { useMemo } from 'react';
|
||||
import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext';
|
||||
import AssetCollectionModal from '@app/homeV3/modules/assetCollection/AssetCollectionModal';
|
||||
import DocumentationModal from '@app/homeV3/modules/documentation/DocumentationModal';
|
||||
import LinkModal from '@app/homeV3/modules/link/LinkModal';
|
||||
|
||||
import { DataHubPageModuleType } from '@types';
|
||||
|
||||
@ -16,6 +17,8 @@ export default function ModuleModalMapper() {
|
||||
// TODO: add support of other module types
|
||||
case DataHubPageModuleType.AssetCollection:
|
||||
return AssetCollectionModal;
|
||||
case DataHubPageModuleType.Link:
|
||||
return LinkModal;
|
||||
case DataHubPageModuleType.RichText:
|
||||
return DocumentationModal;
|
||||
default:
|
||||
|
||||
@ -14,6 +14,7 @@ interface Props {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
onUpsert: () => void;
|
||||
width?: string;
|
||||
submitButtonProps?: Partial<ModalButton>;
|
||||
}
|
||||
|
||||
@ -22,6 +23,7 @@ export default function BaseModuleModal({
|
||||
subtitle,
|
||||
children,
|
||||
onUpsert,
|
||||
width,
|
||||
submitButtonProps,
|
||||
}: React.PropsWithChildren<Props>) {
|
||||
const {
|
||||
@ -54,7 +56,7 @@ export default function BaseModuleModal({
|
||||
onCancel={close}
|
||||
maskClosable={false} // to avoid accidental clicks that closes the modal
|
||||
bodyStyle={modalBodyStyles}
|
||||
width="70%"
|
||||
width={width || '90%'}
|
||||
style={{ maxWidth: 1100 }}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -81,3 +81,11 @@ export const DEFAULT_GLOBAL_MODULE_TYPES: DataHubPageModuleType[] = [
|
||||
DataHubPageModuleType.OwnedAssets,
|
||||
DataHubPageModuleType.Domains,
|
||||
];
|
||||
|
||||
export const LARGE_MODULE_TYPES: DataHubPageModuleType[] = [
|
||||
DataHubPageModuleType.OwnedAssets,
|
||||
DataHubPageModuleType.Domains,
|
||||
DataHubPageModuleType.AssetCollection,
|
||||
DataHubPageModuleType.Hierarchy,
|
||||
DataHubPageModuleType.RichText,
|
||||
];
|
||||
|
||||
38
datahub-web-react/src/app/homeV3/modules/link/LinkForm.tsx
Normal file
38
datahub-web-react/src/app/homeV3/modules/link/LinkForm.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { Input, TextArea } from '@components';
|
||||
import { Form, FormInstance } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import { LinkModuleParams } from '@types';
|
||||
|
||||
interface Props {
|
||||
form: FormInstance;
|
||||
formValues?: LinkModuleParams;
|
||||
}
|
||||
|
||||
export default function LinkForm({ form, formValues }: Props) {
|
||||
return (
|
||||
<Form form={form} initialValues={formValues}>
|
||||
<Form.Item
|
||||
name="linkUrl"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter the link URL',
|
||||
},
|
||||
{
|
||||
type: 'url',
|
||||
message: 'Please enter a valid URL',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input label="Link" placeholder="https://www.datahub.com" isRequired />
|
||||
</Form.Item>
|
||||
<Form.Item name="imageUrl">
|
||||
<Input label="Image URL (Optional)" placeholder="Your image URL" />
|
||||
</Form.Item>
|
||||
<Form.Item name="description">
|
||||
<TextArea label="Description (Optional)" placeholder="Add description..." />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
69
datahub-web-react/src/app/homeV3/modules/link/LinkModal.tsx
Normal file
69
datahub-web-react/src/app/homeV3/modules/link/LinkModal.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import { Form } from 'antd';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext';
|
||||
import BaseModuleModal from '@app/homeV3/moduleModals/common/BaseModuleModal';
|
||||
import ModuleDetailsForm from '@app/homeV3/moduleModals/common/ModuleDetailsForm';
|
||||
import LinkForm from '@app/homeV3/modules/link/LinkForm';
|
||||
|
||||
import { DataHubPageModuleType } from '@types';
|
||||
|
||||
const ModalContent = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export default function LinkModal() {
|
||||
const {
|
||||
upsertModule,
|
||||
moduleModalState: { position, close, isEditing, initialState },
|
||||
} = usePageTemplateContext();
|
||||
const [form] = Form.useForm();
|
||||
const currentName = initialState?.properties.name || '';
|
||||
const linkParams = initialState?.properties?.params?.linkParams;
|
||||
const urn = initialState?.urn;
|
||||
|
||||
/* TODO: Uncomment to validate and disable button once the Rich Text module PR is merged
|
||||
|
||||
const titleValue = Form.useWatch('title', form);
|
||||
const linkUrlValue = Form.useWatch('link', form);
|
||||
const isDisabled = !titleValue?.trim() || !linkUrlValue?.trim(); */
|
||||
|
||||
const handleUpsertDocumentationModule = () => {
|
||||
form.validateFields().then((values) => {
|
||||
const { name, linkUrl, imageUrl, description } = values;
|
||||
upsertModule({
|
||||
urn,
|
||||
name,
|
||||
position: position ?? {},
|
||||
type: DataHubPageModuleType.Link,
|
||||
params: {
|
||||
linkParams: {
|
||||
linkUrl,
|
||||
imageUrl,
|
||||
description,
|
||||
},
|
||||
},
|
||||
});
|
||||
close();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseModuleModal
|
||||
title={`${isEditing ? 'Edit' : 'Add'} Quick Link`}
|
||||
subtitle="Add links to your home page"
|
||||
onUpsert={handleUpsertDocumentationModule}
|
||||
width="40%"
|
||||
/* TODO: Uncomment to validate and disable button once the Rich Text module PR is merged
|
||||
submitButtonProps={{ disabled: isDisabled }} */
|
||||
>
|
||||
<ModalContent>
|
||||
<ModuleDetailsForm form={form} formValues={{ name: currentName }} />
|
||||
<LinkForm form={form} formValues={linkParams ?? undefined} />
|
||||
</ModalContent>
|
||||
</BaseModuleModal>
|
||||
);
|
||||
}
|
||||
75
datahub-web-react/src/app/homeV3/modules/link/LinkModule.tsx
Normal file
75
datahub-web-react/src/app/homeV3/modules/link/LinkModule.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { Icon, Text } from '@components';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import SmallModule from '@app/homeV3/module/components/SmallModule';
|
||||
import { ModuleProps } from '@app/homeV3/module/types';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const RightSection = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const LeftSection = styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const TextSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const Image = styled.img`
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
object-fit: contain;
|
||||
background-color: transparent;
|
||||
`;
|
||||
|
||||
export default function LinkModule(props: ModuleProps) {
|
||||
const { name } = props.module.properties;
|
||||
const { linkParams } = props.module.properties.params;
|
||||
|
||||
function goToLink() {
|
||||
if (linkParams?.linkUrl) {
|
||||
window.open(linkParams.linkUrl, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SmallModule {...props} onClick={goToLink}>
|
||||
<Container>
|
||||
<LeftSection>
|
||||
{linkParams?.imageUrl ? (
|
||||
<Image src={linkParams?.imageUrl} />
|
||||
) : (
|
||||
<Icon icon="LinkSimple" source="phosphor" size="3xl" color="gray" />
|
||||
)}
|
||||
<TextSection>
|
||||
<Text color="gray" colorLevel={600} weight="bold" size="lg" lineHeight="normal">
|
||||
{name}
|
||||
</Text>
|
||||
{linkParams?.description && (
|
||||
<Text color="gray" size="sm">
|
||||
{linkParams?.description}
|
||||
</Text>
|
||||
)}
|
||||
</TextSection>
|
||||
</LeftSection>
|
||||
<RightSection>
|
||||
<a href={linkParams?.linkUrl} target="_blank" rel="noopener noreferrer">
|
||||
<Icon icon="ArrowUpRight" source="phosphor" size="lg" color="gray" />
|
||||
</a>
|
||||
</RightSection>
|
||||
</Container>
|
||||
</SmallModule>
|
||||
);
|
||||
}
|
||||
5
datahub-web-react/src/app/homeV3/modules/link/types.ts
Normal file
5
datahub-web-react/src/app/homeV3/modules/link/types.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export type LinkFormValues = {
|
||||
linkUrl: string;
|
||||
imageUrl?: string;
|
||||
description?: string;
|
||||
};
|
||||
@ -89,3 +89,15 @@ export const EmptyContainer = styled.div`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const FloatingRightHeaderSection = styled.div`
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-right: 16px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
@ -4,6 +4,7 @@ import React, { useCallback, useMemo } from 'react';
|
||||
import { RESET_DROPDOWN_MENU_STYLES_CLASSNAME } from '@components/components/Dropdown/constants';
|
||||
|
||||
import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext';
|
||||
import { LARGE_MODULE_TYPES } from '@app/homeV3/modules/constants';
|
||||
import { ModulesAvailableToAdd } from '@app/homeV3/modules/types';
|
||||
import { convertModuleToModuleInfo } from '@app/homeV3/modules/utils';
|
||||
import GroupItem from '@app/homeV3/template/components/addModuleMenu/components/GroupItem';
|
||||
@ -44,8 +45,15 @@ export default function useAddModuleMenu(
|
||||
const {
|
||||
addModule,
|
||||
moduleModalState: { open: openModal },
|
||||
template,
|
||||
} = usePageTemplateContext();
|
||||
|
||||
const isRowWithLargeModule =
|
||||
position.rowIndex !== undefined &&
|
||||
template?.properties.rows[position.rowIndex]?.modules?.some((module) =>
|
||||
LARGE_MODULE_TYPES.includes(module.properties.type),
|
||||
);
|
||||
|
||||
const handleAddExistingModule = useCallback(
|
||||
(module: PageModuleFragment) => {
|
||||
addModule({
|
||||
@ -73,8 +81,9 @@ export default function useAddModuleMenu(
|
||||
key: 'quick-link',
|
||||
label: <MenuItem description="Choose links that are important" title="Quick Link" icon="LinkSimple" />,
|
||||
onClick: () => {
|
||||
// TODO: open up modal to add a quick link
|
||||
handleOpenCreateModuleModal(DataHubPageModuleType.Link);
|
||||
},
|
||||
disabled: isRowWithLargeModule,
|
||||
};
|
||||
|
||||
const documentation = {
|
||||
@ -160,7 +169,12 @@ export default function useAddModuleMenu(
|
||||
}
|
||||
|
||||
return { items };
|
||||
}, [modulesAvailableToAdd.adminCreatedModules, handleAddExistingModule, handleOpenCreateModuleModal]);
|
||||
}, [
|
||||
isRowWithLargeModule,
|
||||
modulesAvailableToAdd.adminCreatedModules,
|
||||
handleOpenCreateModuleModal,
|
||||
handleAddExistingModule,
|
||||
]);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
@ -36,6 +36,11 @@ fragment PageModule on DataHubPageModule {
|
||||
assetCollectionParams {
|
||||
assetUrns
|
||||
}
|
||||
linkParams {
|
||||
linkUrl
|
||||
imageUrl
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,9 @@ record DataHubPageModuleParams {
|
||||
* The params required if the module is type LINK
|
||||
*/
|
||||
linkParams: optional record LinkModuleParams {
|
||||
linkUrn: Urn
|
||||
linkUrl: string
|
||||
imageUrl: optional string
|
||||
description: optional string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user