Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(propagation): UI for rendering propagated column documentation #11047

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
55a94e7
UI for Documentation Propagation
samblackk Jul 30, 2024
fc325d2
Cypress prettier format fix
samblackk Jul 30, 2024
53f462b
Adding some fluff
Jul 31, 2024
ca9b85a
adding propagation action
Jul 31, 2024
08bcea4
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Jul 31, 2024
2cc7081
UI for Documentation Propagation
samblackk Jul 30, 2024
15c2580
Cypress prettier format fix
samblackk Jul 30, 2024
23eba77
Adding some fluff
Jul 31, 2024
907cba4
adding propagation action
Jul 31, 2024
486f7bb
Adding
Jul 31, 2024
2773e04
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Jul 31, 2024
3b3ef05
Update docPropagation.js
jjoyce0510 Jul 31, 2024
0598921
Update DescriptionModal.tsx
jjoyce0510 Jul 31, 2024
1c86fdd
Adding propagation details fixes
Jul 31, 2024
c1da3d7
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Jul 31, 2024
215a31a
pushing lint fix
Aug 9, 2024
bf3b739
Adding lint fixes
Aug 9, 2024
d1487bc
Final docs and refactor
Aug 10, 2024
54bdf6b
Fint lint
Aug 10, 2024
e32eef2
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 10, 2024
86db1bc
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 10, 2024
298e9e4
Fixing lint
Aug 10, 2024
c4a313b
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 10, 2024
edbc0db
Adding fixes
Aug 20, 2024
b0c61a1
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 20, 2024
5945235
Merge branch 'master' into jj--propagation-doc-action-ui-takeover
jjoyce0510 Aug 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public CompletableFuture<DocPropagationSettings> get(final DataFetchingEnvironme
final GlobalSettingsInfo globalSettings =
_settingsService.getGlobalSettings(context.getOperationContext());
final DocPropagationSettings defaultSettings = new DocPropagationSettings();
defaultSettings.setDocColumnPropagation(true);
// TODO: Enable by default. Currently the automation trusts the settings aspect, which
// does not have this.
defaultSettings.setDocColumnPropagation(false);
return globalSettings != null && globalSettings.hasDocPropagation()
? mapDocPropagationSettings(globalSettings.getDocPropagation())
: defaultSettings;
Expand Down
2 changes: 1 addition & 1 deletion datahub-web-react/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ module.exports = {
],
'vitest/prefer-to-be': 'off',
'@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: false }],
'react-refresh/only-export-components': ['warn', { 'allowConstantExport': true }],
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
settings: {
react: {
Expand Down
130 changes: 67 additions & 63 deletions datahub-web-react/README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
---
title: "datahub-web-react"
title: 'datahub-web-react'
---

# DataHub React App

## About
This module contains a React application that serves as the DataHub UI.

Feel free to take a look around, deploy, and contribute.
This module contains a React application that serves as the DataHub UI.

Feel free to take a look around, deploy, and contribute.

## Functional Goals

The initial milestone for the app was to achieve functional parity with the previous Ember app. This meant supporting

- Dataset Profiles, Search, Browse Experience
- User Profiles, Search
- LDAP Authentication Flow
- Dataset Profiles, Search, Browse Experience
- User Profiles, Search
- LDAP Authentication Flow

This has since been achieved. The new set of functional goals are reflected in the latest version of the [DataHub Roadmap](../docs/roadmap.md).
This has since been achieved. The new set of functional goals are reflected in the latest version of the [DataHub Roadmap](../docs/roadmap.md).

## Design Goals

In building out the client experience, we intend to leverage learnings from the previous Ember-based app and incorporate feedback gathered
from organizations operating DataHub. Two themes have emerged to serve as guideposts:

1. **Configurability**: The client experience should be configurable, such that deploying organizations can tailor certain
aspects to their needs. This includes theme / styling configurability, showing and hiding specific functionality,
customizing copy & logos, etc.

2. **Extensibility**: Extending the *functionality* of DataHub should be as simple as possible. Making changes like
extending an existing entity & adding a new entity should require minimal effort and should be well covered in detailed
documentation.
1. **Configurability**: The client experience should be configurable, such that deploying organizations can tailor certain
aspects to their needs. This includes theme / styling configurability, showing and hiding specific functionality,
customizing copy & logos, etc.
2. **Extensibility**: Extending the _functionality_ of DataHub should be as simple as possible. Making changes like
extending an existing entity & adding a new entity should require minimal effort and should be well covered in detailed
documentation.

## Starting the Application

### Quick Start

Navigate to the `docker` directory and run the following to spin up the react app:

```
./quickstart.sh
```

at `http://localhost:9002`.

If you want to make changes to the UI see them live without having to rebuild the `datahub-frontend-react` docker image, you
Expand All @@ -54,8 +57,9 @@ Optionally you could also start the app with the mock server without running the
### Testing your customizations

There is two options to test your customizations:
* **Option 1**: Initialize the docker containers with the `quickstart.sh` script (or if any custom docker-compose file) and then run `yarn start` in this directory. This will start a forwarding server at `localhost:3000` that will use the `datahub-frontend` server at `http://localhost:9002` to fetch real data.
* **Option 2**: Change the environment variable `REACT_APP_PROXY_TARGET` in the `.env` file to point to your `datahub-frontend` server (ex: https://my_datahub_host.com) and then run `yarn start` in this directory. This will start a forwarding server at `localhost:3000` that will use the `datahub-frontend` server at some domain to fetch real data.

- **Option 1**: Initialize the docker containers with the `quickstart.sh` script (or if any custom docker-compose file) and then run `yarn start` in this directory. This will start a forwarding server at `localhost:3000` that will use the `datahub-frontend` server at `http://localhost:9002` to fetch real data.
- **Option 2**: Change the environment variable `REACT_APP_PROXY_TARGET` in the `.env` file to point to your `datahub-frontend` server (ex: https://my_datahub_host.com) and then run `yarn start` in this directory. This will start a forwarding server at `localhost:3000` that will use the `datahub-frontend` server at some domain to fetch real data.

The option 2 is useful if you want to test your React customizations without having to run the hole DataHub stack locally. However, if you changed other components of the DataHub stack, you will need to run the hole stack locally (building the docker images) and use the option 1.

Expand All @@ -68,10 +72,10 @@ In order to start a server and run frontend unit tests using react-testing-frame
There are also more automated tests using Cypress in the `smoke-test` folder of the repository root.

#### Troubleshooting

`Error: error:0308010C:digital envelope routines::unsupported`: This error message shows up when using Node 17, due to an OpenSSL update related to md5.
The best workaround is to revert to the Active LTS version of Node, 16.13.0 with the command `nvm install 16.13.0` and if necessary reinstall yarn `npm install --global yarn`.


### Theming

#### Customizing your App without rebuilding assets
Expand Down Expand Up @@ -108,74 +112,74 @@ you to terminate and re-run `yarn start` to see updated styles.

The `src` dir of the app is broken down into the following modules

**conf** - Stores global configuration flags that can be referenced across the app. For example, the number of
**conf** - Stores global configuration flags that can be referenced across the app. For example, the number of
search results shown per page, or the placeholder text in the search bar box. It serves as a location where levels
for functional configurability should reside.
for functional configurability should reside.

**app** - Contains all important components of the app. It has a few sub-modules:

- `auth`: Components used to render the user authentication experience.
- `browse`: Shared components used to render the 'browse-by-path' experience. The experience is akin to navigating a filesystem hierarchy.
- `preview`: Shared components used to render Entity 'preview' views. These can appear in search results, browse results,
and within entity profile pages.
- `search`: Shared components used to render the full-text search experience.
- `shared`: Misc. shared components
- `entity`: Contains Entity definitions, where entity-specific functionality resides.
Configuration is provided by implementing the 'Entity' interface. (See DatasetEntity.tsx for example)
There are 2 visual components each entity should supply:
- `profiles`: display relevant details about an individual entity. This serves as the entity's 'profile'.
- `previews`: provide a 'preview', or a smaller details card, containing the most important information about an entity instance.

When rendering a preview, the entity's data and the type of preview (SEARCH, BROWSE, PREVIEW) are provided. This
- `auth`: Components used to render the user authentication experience.
- `browse`: Shared components used to render the 'browse-by-path' experience. The experience is akin to navigating a filesystem hierarchy.
- `preview`: Shared components used to render Entity 'preview' views. These can appear in search results, browse results,
and within entity profile pages.
- `search`: Shared components used to render the full-text search experience.
- `shared`: Misc. shared components
- `entity`: Contains Entity definitions, where entity-specific functionality resides.
Configuration is provided by implementing the 'Entity' interface. (See DatasetEntity.tsx for example)
There are 2 visual components each entity should supply:

- `profiles`: display relevant details about an individual entity. This serves as the entity's 'profile'.
- `previews`: provide a 'preview', or a smaller details card, containing the most important information about an entity instance.

When rendering a preview, the entity's data and the type of preview (SEARCH, BROWSE, PREVIEW) are provided. This
allows you to optionally customize the way an entities preview is rendered in different views.
- `entity registry`: There's another very important piece of code living within this module: the **EntityRegistry**. This is a layer

- `entity registry`: There's another very important piece of code living within this module: the **EntityRegistry**. This is a layer
of abstraction over the intimate details of rendering a particular entity. It is used
to render a view associated with a particular entity type (user, dataset, etc.).



<p align="center">
<img width="70%" src="https://raw.githubusercontent.com/datahub-project/static-assets/main/imgs/entity-registry.png"/>
</p>

**graphql** - The React App talks to the `dathub-frontend` server using GraphQL. This module is where the *queries* issued
against the server are defined. Once defined, running `yarn run generate` will code-gen TypeScript objects to make invoking
**graphql** - The React App talks to the `dathub-frontend` server using GraphQL. This module is where the _queries_ issued
against the server are defined. Once defined, running `yarn run generate` will code-gen TypeScript objects to make invoking
these queries extremely easy. An example can be found at the top of `SearchPage.tsx.`

**images** - Images to be displayed within the app. This is where one would place a custom logo image.
**images** - Images to be displayed within the app. This is where one would place a custom logo image.

## Adding an Entity

The following outlines a series of steps required to introduce a new entity into the React app:

1. Declare the GraphQL Queries required to display the new entity
- If search functionality should be supported, extend the "search" query within `search.graphql` to fetch the new
1. Declare the GraphQL Queries required to display the new entity

- If search functionality should be supported, extend the "search" query within `search.graphql` to fetch the new
entity data.
- If browse functionality should be supported, extend the "browse" query within `browse.graphql` to fetch the new
entity data.
- If browse functionality should be supported, extend the "browse" query within `browse.graphql` to fetch the new
entity data.
- If display a 'profile' should be supported (most often), introduce a new `<entity-name>.graphql` file that contains a
`get` query to fetch the entity by primary key (urn).

Note that your new entity *must* implement the `Entity` GraphQL type interface, and thus must have a corresponding
`EntityType`.


2. Implement the `Entity` interface
- If display a 'profile' should be supported (most often), introduce a new `<entity-name>.graphql` file that contains a
`get` query to fetch the entity by primary key (urn).

Note that your new entity _must_ implement the `Entity` GraphQL type interface, and thus must have a corresponding
`EntityType`.

2. Implement the `Entity` interface

- Create a new folder under `src/components/entity` corresponding to your entity
- Create a class that implements the `Entity` interface (example: `DatasetEntity.tsx`)
- Provide an implementation each method defined on the interface.
- This class specifies whether your new entity should be searchable & browsable, defines the names used to
identify your entity when instances are rendered in collection / when entity appears
in the URL path, and provides the ability to render your entity given data returned by the GQL API.

- Provide an implementation each method defined on the interface.
- This class specifies whether your new entity should be searchable & browsable, defines the names used to
identify your entity when instances are rendered in collection / when entity appears
in the URL path, and provides the ability to render your entity given data returned by the GQL API.

3. Register the new entity in the `EntityRegistry`
- Update `App.tsx` to register an instance of your new entity. Now your entity will be accessible via the registry
- Update `App.tsx` to register an instance of your new entity. Now your entity will be accessible via the registry
and appear in the UI. To manually retrieve the info about your entity or others, simply use an instance
of the `EntityRegistry`, which is provided via `ReactContext` to *all* components in the hierarchy.
of the `EntityRegistry`, which is provided via `ReactContext` to _all_ components in the hierarchy.
For example
```
entityRegistry.getCollectionName(EntityType.YOUR_NEW_ENTITY)
```
That's it! For any questions, do not hesitate to reach out on the DataHub Slack community in #datahub-react.
```
entityRegistry.getCollectionName(EntityType.YOUR_NEW_ENTITY)
```

That's it! For any questions, do not hesitate to reach out on the DataHub Slack community in #datahub-react.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import styled from 'styled-components';
import { FetchResult } from '@apollo/client';

import { UpdateDatasetMutation } from '../../../../../../graphql/dataset.generated';
import { StringMapEntry } from '../../../../../../types.generated';
import PropagationDetails from '../../../../shared/propagation/PropagationDetails';
import UpdateDescriptionModal from '../../../../shared/components/legacy/DescriptionModal';
import StripMarkdownText, { removeMarkdown } from '../../../../shared/components/styled/StripMarkdownText';
import SchemaEditableContext from '../../../../../shared/SchemaEditableContext';
Expand All @@ -28,6 +30,11 @@ const ExpandedActions = styled.div`
height: 10px;
`;

const DescriptionWrapper = styled.span`
display: inline-flex;
align-items: center;
`;

const DescriptionContainer = styled.div`
position: relative;
display: flex;
Expand Down Expand Up @@ -105,6 +112,8 @@ type Props = {
isEdited?: boolean;
isReadOnly?: boolean;
businessAttributeDescription?: string;
isPropagated?: boolean;
sourceDetail?: StringMapEntry[] | null;
};

const ABBREVIATED_LIMIT = 80;
Expand All @@ -120,6 +129,8 @@ export default function DescriptionField({
original,
isReadOnly,
businessAttributeDescription,
isPropagated,
sourceDetail,
}: Props) {
const [showAddModal, setShowAddModal] = useState(false);
const overLimit = removeMarkdown(description).length > 80;
Expand Down Expand Up @@ -163,7 +174,7 @@ export default function DescriptionField({

return (
<DescriptionContainer>
{expanded || !overLimit ? (
{expanded ? (
<>
{!!description && <StyledViewer content={description} readOnly />}
{!!description && (EditButton || overLimit) && (
Expand All @@ -184,25 +195,29 @@ export default function DescriptionField({
</>
) : (
<>
<StripMarkdownText
limit={ABBREVIATED_LIMIT}
readMore={
<>
<Typography.Link
onClick={(e) => {
e.stopPropagation();
handleExpanded(true);
}}
>
Read More
</Typography.Link>
</>
}
suffix={EditButton}
shouldWrap
>
{description}
</StripMarkdownText>
<DescriptionWrapper>
{isPropagated && <PropagationDetails sourceDetail={sourceDetail} />}
&nbsp;
<StripMarkdownText
limit={ABBREVIATED_LIMIT}
readMore={
<>
<Typography.Link
onClick={(e) => {
e.stopPropagation();
handleExpanded(true);
}}
>
Read More
</Typography.Link>
</>
}
suffix={EditButton}
shouldWrap
>
{description}
</StripMarkdownText>
</DescriptionWrapper>
</>
)}
{isEdited && <EditedLabel>(edited)</EditedLabel>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,29 @@ const StyledViewer = styled(Editor)`
}
`;

const OriginalDocumentation = styled(Form.Item)`
margin-bottom: 0;
`;

type Props = {
title: string;
description?: string | undefined;
original?: string | undefined;
propagatedDescription?: string | undefined;
onClose: () => void;
onSubmit: (description: string) => void;
isAddDesc?: boolean;
};

export default function UpdateDescriptionModal({ title, description, original, onClose, onSubmit, isAddDesc }: Props) {
export default function UpdateDescriptionModal({
title,
description,
original,
propagatedDescription,
onClose,
onSubmit,
isAddDesc,
}: Props) {
const [updatedDesc, setDesc] = useState(description || original || '');

const handleEditorKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
Expand Down Expand Up @@ -72,9 +85,14 @@ export default function UpdateDescriptionModal({ title, description, original, o
/>
</Form.Item>
{!isAddDesc && description && original && (
<Form.Item label={<FormLabel>Original:</FormLabel>}>
<OriginalDocumentation label={<FormLabel>Original:</FormLabel>}>
<StyledViewer content={original || ''} readOnly />
</Form.Item>
</OriginalDocumentation>
)}
{!isAddDesc && description && propagatedDescription && (
<OriginalDocumentation label={<FormLabel>Propagated:</FormLabel>}>
<StyledViewer content={propagatedDescription || ''} readOnly />
</OriginalDocumentation>
)}
</Form>
</Modal>
Expand Down
Loading
Loading