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

Show profile and catalog inheritance #186

Merged
merged 36 commits into from
Dec 1, 2021

Conversation

mikeisen1
Copy link
Contributor

Create a react component, OSCALProfileCatalogInheritance, which displays the tree of inherited profiles and catalogs for any Component Definition, Profile, or SSP. This allows us to view that inheritance tree, providing users greater insight into which profiles and catalogs were used in the creation of a given OSCAL document.

Additionally created a storybook page for the new component so any developer can view how it looks and functions and potentially reuse it in their code.

closes #161

Display the tree of inherited profiles and catalogs for any
profile json document in the Profile viewer.
Display the tree of inherited profiles and catalogs for any
component definition json document in the Component Viewer.

Add rule to linting json file that forces the linter to
allow the reassignment of function parameters.
Display the tree on inherited profiles and catalogs for any
SSP json document in the SSP Viewer.

Remove the original document title from being displayed in
the inherited profiles and catalogs.
Identify whether an inherited profile or catalog is a
profile or catalog, making it easy to identify the
document type.
Change the collapse icon to ExpandLess to use an icon more
representative of the action taken.

Use IconButton in order to give labels to the icons, which
is important for testing purposes.
Create tests for the newly added react component to
ensure it functions as expected.
@mikeisen1 mikeisen1 added the enhancement New feature or request label Nov 15, 2021
@mikeisen1 mikeisen1 self-assigned this Nov 15, 2021
.eslintrc.json Outdated
@@ -25,7 +25,8 @@
"react/prop-types": [0],
"react/destructuring-assignment": [0],
"react/jsx-props-no-spreading": "off",
"prettier/prettier": "error"
"prettier/prettier": "error",
"no-param-reassign": 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the addition of this. Why don't we just use a different name for the variable inside the function rather than reusing a parameter name? Shadowing variables is always a little iffy.

Remove the no-param-reassign linting rule as we
do not want to shadow variables.
@mikeisen1
Copy link
Contributor Author

mikeisen1 commented Nov 15, 2021

Here are some of the other options for the expand/collapse icons. Just wanted to put them here in case anyone thought one of these could be better than the ones currently used.

Expand icon: Add, ControlPoint, MoreHoriz, MoreVert, UnfoldMore, ExpandMore

Collapse icon: Remove, Unfold Less, ExpandLess, Close, Cancel, HighlightOff, Remove

You can easily view these visually here by searching for the above icons in the search box.

return null;
}

const generateLabel = (title, type) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be pulled out of renderTree?

<TreeItem
nodeId={idReassign[0].toString()}
label={generateLabel(node.title, node.type)}
expandIcon={
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just the the defaults on TreeView?

Copy link
Contributor Author

@mikeisen1 mikeisen1 Nov 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose not to because if we use the TreeView default icon properties, then all icons will have the same value for the aria-label property. That won't go well for testing purposes so if we want to be able to test the expansion and collapse of the tree, then we can't use the TreeView defaults. If we don't care about that, then the defaults are fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we don't want to be required to rely on aria labels, instead taking an approach of finding the expected text then the closest button.

As mentioned elsewhere on this PR, we also want the tree to be expanded by default, so I'm not sure will need to test clicking on these at all.


test("OSCALProfileCatalogInheritance displays nested inherited objects", async () => {
profileCatalogInheritanceRenderer();
const inheritanceButton = await screen.findByRole(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want all nodes in the tree expanded by default so this may not be needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like it would make sense to at least test the expansion and collapse of the tree to ensure everything works and is displayed as expected in both cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this test can be rewritten once everything is expanded by default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that's fine.

@@ -27,17 +27,19 @@ import getUriFromBackMatterByHref from "./OSCALBackMatterUtils";
* @see {@link https://pages.nist.gov/OSCAL/documentation/schema/profile-layer/profile/xml-schema/#global_import}
* @see {@link https://pages.nist.gov/OSCAL/documentation/schema/profile-layer/profile/xml-schema/#global_back-matter_h2}
*/
export default function OSCALResolveProfileOrCatalogUrlControls(
export default async function OSCALResolveProfileOrCatalogUrlControls(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we're introducing asyncs and awaits for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was some issue when I was first doing the task that async/await seemed to resolve. It isn't needed I just forgot to remove them from the code.

Comment on lines 27 to 28
id[0] += 1;
idReassign[0] += 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not really short on characters here. How about inheritedNodeId or just nodeId to give it a more descriptive name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thought here would be to add the UUID of the OSCAL document to the inheritance object, but we also talk about the possibility of using default icons at the TreeView level, so this ID may not be needed at all.

Add second React component, one for the TreeView that displays
the inherited profiles and catalogs, so the defaultExpanded
prop of TreeView works as expected, allowing us to see the
expanded tree by default and collapse the expanded tree as
desired. TreeView has an expanded prop that doesn't require
a second React component but that doesn't allow for the
collapse of TreeView nodes.

Clean up component, testing, and other files to remove code
that was not necessary and rename functions and variables
to improve readability.
Remove async/await, as they are not needed and do not
impact the result.
Test that nested profiles/catalogs will be displayed.
Copy link
Contributor

@hreineck hreineck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Both the Viewer and Storybook are working locally for me.
Just a few small changes/questions.

Comment on lines 26 to 39
setInheritedProfilesAndCatalogs(
OSCALComponentResolveSources(
props.componentDefinition,
props.parentUrl,
() => {
setIsLoaded(true);
props.onResolutionComplete();
},
(errorReturned) => {
setError(errorReturned);
setIsLoaded(true);
props.onResolutionComplete();
}
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting a bit convoluted. Instead of having a method that takes in the return of the existing OSCALComponentResolveSources method, could we have an overall "resolve" wrapper method like the other OSCAL types do?

import ListSubheader from "@material-ui/core/ListSubheader";

export default function OSCALProfileCatalogInheritanceTree(props) {
const tree =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the const necessary? Why not just return this object?

<ExpandLessIcon />
</IconButton>
}
defaultExpanded={props.validIds}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just putting this out here - is the ability to actually collapse the tree necessary? If it makes things simpler to have it permanently expanded that could work as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that it's absolutely necessary but if we can allow users to collapse the tree we should provide it as it's something, albeit small, that could improve user experience.

Remove intialization and use of a variable that was
not needed. Remove return from a function and instead
pass in a function that sets a state variable so
a component will rerender.
Copy link
Contributor

@hreineck hreineck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!


export default function OSCALProfileCatalogInheritance(props) {
const classes = useStyles();
const [validIds, setValidIds] = useState([false, []]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need all of these instances of IDs separately rather than just using the UUID already in the inherited objects?

Copy link
Contributor Author

@mikeisen1 mikeisen1 Nov 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to ensure each nodeId in the tree is unique? We could have the same profile or catalog in different parts of the tree which could cause repeated nodeIds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're setting node IDs and keys we do want them to be unique.

Are we sure we need useState here?

I'd also recommend changing these to something like expandedIds everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need useState so when we set the expanded ids, we re-render the component. If we do not re-render, the tree will not be expanded by default.


export default function OSCALProfileCatalogInheritance(props) {
const classes = useStyles();
const [validIds, setValidIds] = useState([false, []]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're setting node IDs and keys we do want them to be unique.

Are we sure we need useState here?

I'd also recommend changing these to something like expandedIds everywhere.

Remove the second react component that was created,
as it was not needed and we want to avoid creating
additional, unnecessary components.
Rename variable to make code more readable.
Relocate call of setInheritedProfilesAndCatalogs to within
onSuccess and remove that parameter from util resolver
functions.
Copy link
Contributor

@rgauss rgauss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor rendering changes:

  1. I think the tree needs more of a left margin. The expand arrow currently bumps up against the card edge.
  2. The header is sticky rather than scrolling with the page

Create a left margin for the tree so the expand and
collapse icons do not brush against the edge.

Make the header scroll with the page instead of
being sticky.
Copy link
Contributor

@rgauss rgauss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks for working through all the changes.

@rgauss rgauss merged commit bc46b8f into develop Dec 1, 2021
@rgauss rgauss deleted the show-profile-catalog-inheritance branch December 1, 2021 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Show Profile/Catalog Inheritance
4 participants