Skip to content

Commit 17ca9dd

Browse files
jjoyce0510cClaudeJohn JoyceJohn JoyceJohn Joyce
authored
feat(docs): Introducing UI flows for Context Base V1 (#15279)
Co-authored-by: cclaude-session <[email protected]> Co-authored-by: John Joyce <[email protected]> Co-authored-by: John Joyce <[email protected]> Co-authored-by: John Joyce <[email protected]> Co-authored-by: John Joyce <[email protected]> Co-authored-by: John Joyce <[email protected]>
1 parent 0ee7d8c commit 17ca9dd

File tree

89 files changed

+9592
-69
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+9592
-69
lines changed

datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/knowledge/SearchDocumentsResolver.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,15 @@ private Filter buildCombinedFilter(SearchDocumentsInput input) {
175175
criteria.add(CriterionUtils.buildCriterion("state", Condition.EQUAL, stateStrings));
176176
}
177177

178+
// Add source type filter if provided (if null, search all)
179+
if (input.getSourceType() != null) {
180+
criteria.add(
181+
CriterionUtils.buildCriterion(
182+
"sourceType",
183+
Condition.EQUAL,
184+
Collections.singletonList(input.getSourceType().toString())));
185+
}
186+
178187
// Exclude documents that are drafts by default, unless explicitly requested
179188
if (input.getIncludeDrafts() == null || !input.getIncludeDrafts()) {
180189
Criterion notDraftCriterion = new Criterion();

datahub-graphql-core/src/main/resources/documents.graphql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,12 @@ input SearchDocumentsInput {
566566
"""
567567
states: [DocumentState!]
568568

569+
"""
570+
Optional document source type to filter by.
571+
If not provided, searches all documents regardless of source.
572+
"""
573+
sourceType: DocumentSourceType
574+
569575
"""
570576
Whether to include draft documents in the search results.
571577
Draft documents have draftOf set and are hidden from normal browsing by default.

datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/knowledge/SearchDocumentsResolverTest.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
import com.google.common.collect.ImmutableList;
1010
import com.linkedin.common.urn.UrnUtils;
1111
import com.linkedin.datahub.graphql.QueryContext;
12+
import com.linkedin.datahub.graphql.generated.DocumentSourceType;
1213
import com.linkedin.datahub.graphql.generated.DocumentState;
1314
import com.linkedin.datahub.graphql.generated.SearchDocumentsInput;
1415
import com.linkedin.datahub.graphql.generated.SearchDocumentsResult;
1516
import com.linkedin.entity.EntityResponse;
1617
import com.linkedin.entity.client.EntityClient;
18+
import com.linkedin.metadata.query.filter.Condition;
19+
import com.linkedin.metadata.query.filter.Filter;
1720
import com.linkedin.metadata.search.SearchEntity;
1821
import com.linkedin.metadata.search.SearchEntityArray;
1922
import com.linkedin.metadata.search.SearchResult;
@@ -24,6 +27,7 @@
2427
import java.util.HashMap;
2528
import java.util.Map;
2629
import java.util.concurrent.CompletionException;
30+
import org.mockito.ArgumentCaptor;
2731
import org.testng.annotations.BeforeMethod;
2832
import org.testng.annotations.Test;
2933

@@ -254,4 +258,72 @@ public void testSearchDocumentsIncludeDrafts() throws Exception {
254258
.searchDocuments(
255259
any(OperationContext.class), eq("test query"), any(), any(), eq(0), eq(10));
256260
}
261+
262+
@Test
263+
public void testSearchDocumentsWithSourceType() throws Exception {
264+
QueryContext mockContext = getMockAllowContext();
265+
when(mockEnv.getContext()).thenReturn(mockContext);
266+
when(mockEnv.getArgument(eq("input"))).thenReturn(input);
267+
268+
input.setSourceType(DocumentSourceType.NATIVE);
269+
270+
resolver.get(mockEnv).get();
271+
272+
ArgumentCaptor<Filter> filterCaptor = ArgumentCaptor.forClass(Filter.class);
273+
verify(mockService, times(1))
274+
.searchDocuments(
275+
any(OperationContext.class),
276+
eq("test query"),
277+
filterCaptor.capture(),
278+
any(),
279+
eq(0),
280+
eq(10));
281+
282+
Filter filter = filterCaptor.getValue();
283+
assertNotNull(filter, "Filter should not be null");
284+
assertNotNull(filter.getOr(), "Filter OR clause should not be null");
285+
286+
boolean hasSourceType =
287+
filter.getOr().stream()
288+
.flatMap(cc -> cc.getAnd().stream())
289+
.anyMatch(
290+
c ->
291+
"sourceType".equals(c.getField())
292+
&& c.getValues() != null
293+
&& c.getValues().contains("NATIVE")
294+
&& c.getCondition() == Condition.EQUAL);
295+
296+
assertTrue(hasSourceType, "Filter should contain sourceType=NATIVE");
297+
}
298+
299+
@Test
300+
public void testSearchDocumentsDefaultSourceType() throws Exception {
301+
QueryContext mockContext = getMockAllowContext();
302+
when(mockEnv.getContext()).thenReturn(mockContext);
303+
when(mockEnv.getArgument(eq("input"))).thenReturn(input);
304+
305+
input.setSourceType(null);
306+
307+
resolver.get(mockEnv).get();
308+
309+
ArgumentCaptor<Filter> filterCaptor = ArgumentCaptor.forClass(Filter.class);
310+
verify(mockService, times(1))
311+
.searchDocuments(
312+
any(OperationContext.class),
313+
eq("test query"),
314+
filterCaptor.capture(),
315+
any(),
316+
eq(0),
317+
eq(10));
318+
319+
Filter filter = filterCaptor.getValue();
320+
if (filter != null) {
321+
boolean hasSourceType =
322+
filter.getOr().stream()
323+
.flatMap(cc -> cc.getAnd().stream())
324+
.anyMatch(c -> "sourceType".equals(c.getField()));
325+
326+
assertFalse(hasSourceType, "Filter should NOT contain sourceType when not provided");
327+
}
328+
}
257329
}

datahub-web-react/src/Mocks.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3796,6 +3796,7 @@ export const mocks = [
37963796
manageApplications: true,
37973797
manageFeatures: true,
37983798
manageHomePageTemplates: true,
3799+
manageDocuments: true,
37993800
},
38003801
},
38013802
},

datahub-web-react/src/alchemy-components/components/Editor/Editor.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const Editor = forwardRef((props: EditorProps, ref) => {
5555
onKeyDown,
5656
hideBorder,
5757
uploadFileProps,
58+
fixedBottomToolbar,
5859
} = props;
5960
const { manager, state, getContext } = useRemirror({
6061
extensions: () => [
@@ -113,6 +114,7 @@ export const Editor = forwardRef((props: EditorProps, ref) => {
113114
$readOnly={readOnly}
114115
onKeyDown={onKeyDown}
115116
$hideBorder={hideBorder}
117+
$fixedBottomToolbar={fixedBottomToolbar}
116118
>
117119
<ThemeProvider theme={EditorTheme}>
118120
<Remirror
@@ -124,7 +126,7 @@ export const Editor = forwardRef((props: EditorProps, ref) => {
124126
>
125127
{!readOnly && (
126128
<>
127-
<Toolbar styles={toolbarStyles} />
129+
<Toolbar styles={toolbarStyles} fixedBottom={fixedBottomToolbar} />
128130
<CodeBlockToolbar />
129131
{!hideHighlightToolbar && <FloatingToolbar />}
130132
<TableComponents tableCellMenuProps={{ Component: TableCellMenu }} />

datahub-web-react/src/alchemy-components/components/Editor/EditorTheme.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ export const EditorTheme: RemirrorThemeType = {
4747
},
4848
};
4949

50-
export const EditorContainer = styled.div<{ $readOnly?: boolean; $hideBorder?: boolean }>`
50+
export const EditorContainer = styled.div<{
51+
$readOnly?: boolean;
52+
$hideBorder?: boolean;
53+
$fixedBottomToolbar?: boolean;
54+
}>`
5155
${extensionBlockquoteStyledCss}
5256
${extensionCalloutStyledCss}
5357
${extensionCodeBlockStyledCss}
@@ -65,6 +69,7 @@ export const EditorContainer = styled.div<{ $readOnly?: boolean; $hideBorder?: b
6569
flex: 1 1 auto;
6670
border: ${(props) => (props.$readOnly || props.$hideBorder ? `none` : `1px solid ${ANTD_GRAY[4.5]}`)};
6771
border-radius: 12px;
72+
padding-bottom: ${(props) => (props.$fixedBottomToolbar ? '100px' : '0')};
6873
6974
.remirror-theme,
7075
.remirror-editor-wrapper {
@@ -135,4 +140,8 @@ export const EditorContainer = styled.div<{ $readOnly?: boolean; $hideBorder?: b
135140
.remirror-floating-popover {
136141
z-index: 100;
137142
}
143+
144+
.remirror-is-empty::before {
145+
font-style: normal !important;
146+
}
138147
`;

datahub-web-react/src/alchemy-components/components/Editor/toolbar/Toolbar.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,30 @@ import { HeadingMenu } from '@components/components/Editor/toolbar/HeadingMenu';
2626
import { useAppConfig } from '@app/useAppConfig';
2727
import colors from '@src/alchemy-components/theme/foundations/colors';
2828

29-
const Container = styled.div`
30-
position: sticky;
31-
top: 0;
32-
z-index: 99;
29+
const Container = styled.div<{ $fixedBottom?: boolean }>`
30+
position: ${(props) => (props.$fixedBottom ? 'fixed' : 'sticky')};
31+
${(props) => (props.$fixedBottom ? 'bottom: 48px;' : 'top: 0;')}
32+
${(props) =>
33+
props.$fixedBottom
34+
? 'left: 50%; transform: translateX(-40%); max-width: 800px; width: fit-content;'
35+
: 'width: 100%;'}
36+
z-index: ${(props) => (props.$fixedBottom ? '1000' : '99')};
3337
background-color: white;
34-
border-top-left-radius: 12px;
35-
border-top-right-radius: 12px;
38+
${(props) =>
39+
props.$fixedBottom
40+
? 'border-radius: 12px; border: 1px solid #e8e8e8;'
41+
: 'border-top-left-radius: 12px; border-top-right-radius: 12px;'}
3642
padding: 8px !important;
3743
& button {
3844
line-height: 0;
3945
}
4046
display: flex;
4147
justify-content: start;
4248
align-items: center;
43-
box-shadow: 0 4px 6px -4px rgba(0, 0, 0, 0.1);
44-
width: 100%;
49+
${(props) =>
50+
props.$fixedBottom
51+
? 'box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.12), 0px 2px 6px rgba(0, 0, 0, 0.08);'
52+
: 'box-shadow: 0 4px 6px -4px rgba(0, 0, 0, 0.1);'}
4553
`;
4654

4755
const InnerContainer = styled.div`
@@ -57,9 +65,10 @@ const CustomDivider = styled(Divider)`
5765

5866
interface Props {
5967
styles?: React.CSSProperties;
68+
fixedBottom?: boolean;
6069
}
6170

62-
export const Toolbar = ({ styles }: Props) => {
71+
export const Toolbar = ({ styles, fixedBottom }: Props) => {
6372
const commands = useCommands();
6473
const active = useActive(true);
6574
const { config } = useAppConfig();
@@ -70,7 +79,7 @@ export const Toolbar = ({ styles }: Props) => {
7079
const shouldShowImageButtonV2 = documentationFileUploadV1 && fileExtension.options.uploadFileProps?.onFileUpload;
7180

7281
return (
73-
<Container style={styles}>
82+
<Container style={styles} $fixedBottom={fixedBottom}>
7483
<InnerContainer>
7584
<FontSizeSelect />
7685
<HeadingMenu />

datahub-web-react/src/alchemy-components/components/Editor/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ export type EditorProps = {
3434
onKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
3535
hideBorder?: boolean;
3636
uploadFileProps?: FileUploadProps;
37+
fixedBottomToolbar?: boolean;
3738
};

datahub-web-react/src/app/AppProviders.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react';
33
import EntityRegistryProvider from '@app/EntityRegistryProvider';
44
import GlobalSettingsProvider from '@app/context/GlobalSettingsProvider';
55
import UserContextProvider from '@app/context/UserContextProvider';
6+
import { DocumentTreeProvider } from '@app/document/DocumentTreeContext';
67
import { NavBarProvider } from '@app/homeV2/layout/navBarRedesign/NavBarContext';
78
import HomePageProvider from '@app/homeV3/context/HomePageProvider';
89
import OnboardingTourProvider from '@app/onboarding/OnboardingTourContextProvider';
@@ -23,21 +24,23 @@ export default function AppProviders({ children }: Props) {
2324
<GlobalSettingsProvider>
2425
<UserContextProvider>
2526
<EntityRegistryProvider>
26-
<BrowserTitleProvider>
27-
<EducationStepsProvider>
28-
<OnboardingTourProvider>
29-
<QuickFiltersProvider>
30-
<SearchContextProvider>
31-
<ReloadableProvider>
32-
<HomePageProvider>
33-
<NavBarProvider>{children}</NavBarProvider>
34-
</HomePageProvider>
35-
</ReloadableProvider>
36-
</SearchContextProvider>
37-
</QuickFiltersProvider>
38-
</OnboardingTourProvider>
39-
</EducationStepsProvider>
40-
</BrowserTitleProvider>
27+
<DocumentTreeProvider>
28+
<BrowserTitleProvider>
29+
<EducationStepsProvider>
30+
<OnboardingTourProvider>
31+
<QuickFiltersProvider>
32+
<SearchContextProvider>
33+
<ReloadableProvider>
34+
<HomePageProvider>
35+
<NavBarProvider>{children}</NavBarProvider>
36+
</HomePageProvider>
37+
</ReloadableProvider>
38+
</SearchContextProvider>
39+
</QuickFiltersProvider>
40+
</OnboardingTourProvider>
41+
</EducationStepsProvider>
42+
</BrowserTitleProvider>
43+
</DocumentTreeProvider>
4144
</EntityRegistryProvider>
4245
</UserContextProvider>
4346
</GlobalSettingsProvider>

datahub-web-react/src/app/analytics/event.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ export enum EventType {
169169
FileDownloadViewEvent,
170170
FileUploadLatencyEvent,
171171
FileDownloadLatencyEvent,
172+
CreateDocumentEvent,
173+
MoveDocumentEvent,
174+
EditDocumentEvent,
175+
DeleteDocumentEvent,
172176
}
173177

174178
/**
@@ -1234,6 +1238,56 @@ export interface FileDownloadLatencyEvent extends BaseEvent {
12341238
duration: number;
12351239
}
12361240

1241+
/**
1242+
* Logged when a user creates a new document.
1243+
*/
1244+
export interface CreateDocumentEvent extends BaseEvent {
1245+
type: EventType.CreateDocumentEvent;
1246+
documentUrn: string;
1247+
documentType?: string;
1248+
hasParent: boolean;
1249+
parentDocumentUrn?: string;
1250+
}
1251+
1252+
/**
1253+
* Logged when a user moves a document to a different parent.
1254+
*/
1255+
export interface MoveDocumentEvent extends BaseEvent {
1256+
type: EventType.MoveDocumentEvent;
1257+
documentUrn: string;
1258+
oldParentDocumentUrn?: string;
1259+
newParentDocumentUrn?: string;
1260+
}
1261+
1262+
/**
1263+
* Describes what kind of edit was made to a document.
1264+
*/
1265+
export enum DocumentEditType {
1266+
Title = 'Title',
1267+
Contents = 'Contents',
1268+
PublishState = 'PublishState',
1269+
Type = 'Type',
1270+
}
1271+
1272+
/**
1273+
* Logged when a user edits a document.
1274+
*/
1275+
export interface EditDocumentEvent extends BaseEvent {
1276+
type: EventType.EditDocumentEvent;
1277+
documentUrn: string;
1278+
editType: DocumentEditType;
1279+
documentType?: string;
1280+
}
1281+
1282+
/**
1283+
* Logged when a user deletes a document.
1284+
*/
1285+
export interface DeleteDocumentEvent extends BaseEvent {
1286+
type: EventType.DeleteDocumentEvent;
1287+
documentUrn: string;
1288+
documentType?: string;
1289+
}
1290+
12371291
/**
12381292
* Event consisting of a union of specific event types.
12391293
*/
@@ -1380,4 +1434,8 @@ export type Event =
13801434
| FileUploadSucceededEvent
13811435
| FileDownloadViewEvent
13821436
| FileUploadLatencyEvent
1383-
| FileDownloadLatencyEvent;
1437+
| FileDownloadLatencyEvent
1438+
| CreateDocumentEvent
1439+
| MoveDocumentEvent
1440+
| EditDocumentEvent
1441+
| DeleteDocumentEvent;

0 commit comments

Comments
 (0)