├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ └── feature.yml ├── dependabot.yml ├── release.yml └── workflows │ ├── add-to-project.yml │ ├── codeql-analysis.yml │ ├── deploy-dev-viewer.yml │ ├── deploy-prod-viewer.yml │ ├── gh-pages.yml │ ├── pr.yml │ ├── release.yml │ ├── update-dependencies.yml │ └── update-types.yml ├── .gitignore ├── .markdownlint.yaml ├── .markdownlintignore ├── .prettierrc ├── .yaml-lint.yml ├── LICENSE ├── README.md ├── buildspec.yml ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── oscal-react-library │ ├── .babelrc.json │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .git-blame-ignore-revs │ ├── .storybook │ │ └── main.ts │ ├── createPalette.d.ts │ ├── custom.d.ts │ ├── docs │ │ └── adding-anchor-links.md │ ├── jest.config.ts │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── common-tests │ │ │ └── UrlParameter.test.js │ │ ├── components │ │ │ ├── ButtonLaunchedDialog.tsx │ │ │ ├── ErrorHandling.tsx │ │ │ ├── Fetchers.js │ │ │ ├── Handlers.js │ │ │ ├── HoverablePopover.test.tsx │ │ │ ├── HoverablePopover.tsx │ │ │ ├── OSCALAnchorLinkHeader.tsx │ │ │ ├── OSCALAppBar.tsx │ │ │ ├── OSCALBackMatter.test.tsx │ │ │ ├── OSCALBackMatter.tsx │ │ │ ├── OSCALCatalog.test.tsx │ │ │ ├── OSCALCatalog.tsx │ │ │ ├── OSCALCatalogBaseline.tsx │ │ │ ├── OSCALCatalogGroup.tsx │ │ │ ├── OSCALCatalogGroups.test.tsx │ │ │ ├── OSCALCatalogGroups.tsx │ │ │ ├── OSCALCatalogManageControl.tsx │ │ │ ├── OSCALCatalogManageGroup.tsx │ │ │ ├── OSCALClass.tsx │ │ │ ├── OSCALComponentDefinition.js │ │ │ ├── OSCALComponentDefinition.test.js │ │ │ ├── OSCALComponentDefinitionComponent.js │ │ │ ├── OSCALComponentDefinitionControlImplementation.js │ │ │ ├── OSCALComponentDefinitionControlImplementation.test.js │ │ │ ├── OSCALControl.test.js │ │ │ ├── OSCALControl.tsx │ │ │ ├── OSCALControlImplementation.js │ │ │ ├── OSCALControlImplementationAdd.js │ │ │ ├── OSCALControlImplementationAdd.test.js │ │ │ ├── OSCALControlImplementationImplReq.js │ │ │ ├── OSCALControlImplementationImplReq.test.js │ │ │ ├── OSCALControlLabel.tsx │ │ │ ├── OSCALControlModification.js │ │ │ ├── OSCALControlParamLegend.js │ │ │ ├── OSCALControlPart.js │ │ │ ├── OSCALControlProse.js │ │ │ ├── OSCALControlProse.test.js │ │ │ ├── OSCALDiagram.js │ │ │ ├── OSCALDiagram.test.js │ │ │ ├── OSCALDrawerSelector.test.tsx │ │ │ ├── OSCALDrawerSelector.tsx │ │ │ ├── OSCALEditableFieldActions.tsx │ │ │ ├── OSCALEditableTextField.test.tsx │ │ │ ├── OSCALEditableTextField.tsx │ │ │ ├── OSCALJsonEditor.js │ │ │ ├── OSCALJsonEditor.test.js │ │ │ ├── OSCALLoader.tsx │ │ │ ├── OSCALLoaderForm.jsx │ │ │ ├── OSCALLoaderStyles.js │ │ │ ├── OSCALMarkupProse.test.tsx │ │ │ ├── OSCALMarkupProse.tsx │ │ │ ├── OSCALMetadata.test.tsx │ │ │ ├── OSCALMetadata.tsx │ │ │ ├── OSCALMetadataCommon.tsx │ │ │ ├── OSCALParam.tsx │ │ │ ├── OSCALPermanentDrawer.tsx │ │ │ ├── OSCALProfile.js │ │ │ ├── OSCALProfile.test.js │ │ │ ├── OSCALProfileCatalogInheritance.js │ │ │ ├── OSCALProfileCatalogInheritance.test.js │ │ │ ├── OSCALProperties.test.tsx │ │ │ ├── OSCALProperties.tsx │ │ │ ├── OSCALResponsibleRoles.js │ │ │ ├── OSCALResponsibleRoles.test.js │ │ │ ├── OSCALRevision.test.tsx │ │ │ ├── OSCALRevision.tsx │ │ │ ├── OSCALSsp.js │ │ │ ├── OSCALSsp.test.js │ │ │ ├── OSCALStyledTooltip.js │ │ │ ├── OSCALSystemCharacteristics.js │ │ │ ├── OSCALSystemCharacteristics.test.js │ │ │ ├── OSCALSystemImplementation.js │ │ │ ├── OSCALSystemImplementation.test.js │ │ │ ├── OSCALSystemImplementationComponents.test.tsx │ │ │ ├── OSCALSystemImplementationComponents.tsx │ │ │ ├── OSCALSystemImplementationInventoryItems.js │ │ │ ├── OSCALSystemImplementationInventoryItems.test.js │ │ │ ├── OSCALSystemImplementationPropertiesTable.js │ │ │ ├── OSCALSystemImplementationTableStyles.js │ │ │ ├── OSCALSystemImplementationUsers.js │ │ │ ├── OSCALSystemImplementationUsers.test.js │ │ │ ├── OscalContext.js │ │ │ ├── images │ │ │ │ ├── EDC_Reverse_Tagline.png │ │ │ │ ├── EDC_Reverse_Tagline.svg │ │ │ │ └── icons │ │ │ │ │ ├── Uploading.svg │ │ │ │ │ ├── XinCircle.svg │ │ │ │ │ ├── blueCircle.svg │ │ │ │ │ ├── catalogs.svg │ │ │ │ │ ├── catalogs_hov.svg │ │ │ │ │ ├── code.svg │ │ │ │ │ ├── component.svg │ │ │ │ │ ├── component_hov.svg │ │ │ │ │ ├── dashboard.svg │ │ │ │ │ ├── dashboard_hov.svg │ │ │ │ │ ├── delete.svg │ │ │ │ │ ├── edit.svg │ │ │ │ │ ├── enterprise.svg │ │ │ │ │ ├── enterprise_hov.svg │ │ │ │ │ ├── expand.svg │ │ │ │ │ ├── export.svg │ │ │ │ │ ├── export_hov.svg │ │ │ │ │ ├── fileUpload.svg │ │ │ │ │ ├── filebucket.svg │ │ │ │ │ ├── gear.svg │ │ │ │ │ ├── gear_hov.svg │ │ │ │ │ ├── greenCheck.svg │ │ │ │ │ ├── greenCircle.svg │ │ │ │ │ ├── indent.svg │ │ │ │ │ ├── info.svg │ │ │ │ │ ├── info_hov.svg │ │ │ │ │ ├── insert.svg │ │ │ │ │ ├── logout.svg │ │ │ │ │ ├── logout_hov.svg │ │ │ │ │ ├── notify.svg │ │ │ │ │ ├── notify_hov.svg │ │ │ │ │ ├── orangeCircleChecked.svg │ │ │ │ │ ├── outdent.svg │ │ │ │ │ ├── plus.svg │ │ │ │ │ ├── plus_hov.svg │ │ │ │ │ ├── projects.svg │ │ │ │ │ ├── projects_hov1.svg │ │ │ │ │ ├── projects_hov2.svg │ │ │ │ │ ├── quote.svg │ │ │ │ │ ├── redCircle.svg │ │ │ │ │ ├── redX.svg │ │ │ │ │ ├── search.svg │ │ │ │ │ ├── search_hov.svg │ │ │ │ │ ├── semiCircleOrange.svg │ │ │ │ │ ├── support.svg │ │ │ │ │ ├── support_hov.svg │ │ │ │ │ ├── tools.svg │ │ │ │ │ ├── tools_hov.svg │ │ │ │ │ ├── trash.svg │ │ │ │ │ ├── trash_hov.svg │ │ │ │ │ ├── upload.svg │ │ │ │ │ ├── upload_hov.svg │ │ │ │ │ └── uploading.svg │ │ │ ├── oscal-utils │ │ │ │ ├── OSCALBackMatterUtils.test.ts │ │ │ │ ├── OSCALBackMatterUtils.ts │ │ │ │ ├── OSCALCatalogUtils.ts │ │ │ │ ├── OSCALComponentResolver.js │ │ │ │ ├── OSCALControlResolver.js │ │ │ │ ├── OSCALLinkUtils.ts │ │ │ │ ├── OSCALMediaTypeUtils.test.ts │ │ │ │ ├── OSCALMediaTypeUtils.ts │ │ │ │ ├── OSCALObjectData.ts │ │ │ │ ├── OSCALProfileResolver.js │ │ │ │ ├── OSCALProfileUtils.js │ │ │ │ ├── OSCALPropUtils.test.ts │ │ │ │ ├── OSCALPropUtils.ts │ │ │ │ ├── OSCALRestUtils.ts │ │ │ │ ├── OSCALSspResolver.js │ │ │ │ └── TestUtils.js │ │ │ └── styles │ │ │ │ ├── CommonPageStyles.js │ │ │ │ ├── OSCALAccordion.tsx │ │ │ │ ├── OSCALAlerts.js │ │ │ │ ├── OSCALButtons.js │ │ │ │ ├── OSCALDialog.js │ │ │ │ ├── OSCALInputs.js │ │ │ │ ├── OSCALStyle.js │ │ │ │ └── OSCALTable.js │ │ ├── index.ts │ │ ├── stories │ │ │ ├── OSCALBackMatter.stories.js │ │ │ ├── OSCALComponentDefinitionComponent.stories.js │ │ │ ├── OSCALComponentDefinitionControlImplementation.stories.js │ │ │ ├── OSCALControl.stories.js │ │ │ ├── OSCALControlImplementation.stories.js │ │ │ ├── OSCALControlImplementationAdd.stories.js │ │ │ ├── OSCALControlModification.stories.js │ │ │ ├── OSCALControlPart.stories.js │ │ │ ├── OSCALControlProse.stories.js │ │ │ ├── OSCALDiagram.stories.js │ │ │ ├── OSCALDocs.stories.mdx │ │ │ ├── OSCALEditableFieldActions.stories.js │ │ │ ├── OSCALEditableTextField.stories.js │ │ │ ├── OSCALMetadata.stories.js │ │ │ ├── OSCALMetadataAddress.stories.js │ │ │ ├── OSCALMetadataEmail.stories.js │ │ │ ├── OSCALMetadataKeyword.stories.js │ │ │ ├── OSCALMetadataKeywords.stories.js │ │ │ ├── OSCALMetadataLocation.stories.js │ │ │ ├── OSCALMetadataLocationContent.stories.js │ │ │ ├── OSCALMetadataParty.stories.js │ │ │ ├── OSCALMetadataTelephone.stories.js │ │ │ ├── OSCALProfileCatalogInheritance.stories.js │ │ │ ├── OSCALProperties.stories.tsx │ │ │ ├── OSCALResponsibleRoles.stories.js │ │ │ ├── OSCALSystemCharacteristics.stories.js │ │ │ ├── OSCALSystemImplementation.stories.js │ │ │ ├── OSCALSystemImplementationComponents.stories.js │ │ │ ├── OSCALSystemImplementationInventoryItems.stories.js │ │ │ └── OSCALSystemImplementationUsers.stories.js │ │ ├── test-data │ │ │ ├── BackMatterData.js │ │ │ ├── CommonData.ts │ │ │ ├── ComponentsData.js │ │ │ ├── ControlsData.js │ │ │ ├── DiagramData.js │ │ │ ├── MetadataData.js │ │ │ ├── ModificationsData.js │ │ │ ├── ProfileData.js │ │ │ ├── SystemData.ts │ │ │ ├── Urls.js │ │ │ └── resources │ │ │ │ └── diagram.png │ │ ├── utils.test.ts │ │ └── utils.ts │ ├── tests │ │ ├── fileTransform.js │ │ └── setupTests.js │ └── tsconfig.json ├── oscal-types │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── oscal.ts │ └── tsconfig.json └── oscal-viewer │ ├── .eslintignore │ ├── .eslintrc.json │ ├── README.md │ ├── craco.config.cjs │ ├── docs │ └── resources │ │ ├── catalog-viewer-screenshot.png │ │ ├── component-viewer-screenshot.png │ │ ├── profile-viewer-screenshot.png │ │ └── ssp-viewer-screenshot.png │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── App.js │ ├── App.test.js │ ├── GlobalStyles.jsx │ ├── images │ └── logo-header.svg │ ├── index.tsx │ ├── logo.svg │ ├── reportWebVitals.js │ ├── setupTests.js │ └── themes │ └── AppTheme.js └── scripts ├── generate-types.ts └── watch-viewer /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | description: Report a bug 4 | labels: 5 | - bug 6 | body: 7 | - type: textarea 8 | id: description 9 | attributes: 10 | label: Description 11 | description: >- 12 | Please provide a general description of the bug you're experiencing 13 | validations: 14 | required: true 15 | 16 | - type: input 17 | id: environment 18 | attributes: 19 | label: Environment 20 | description: >- 21 | If the issue is ocurring in a particular environment, please provide its 22 | name or URL 23 | validations: 24 | required: false 25 | 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: Steps to Reproduce 30 | description: >- 31 | A series of steps that can be taken to reproduce the bug. 32 | 33 | If there is a particular code sample that can be provided, please provide either a 34 | link or a relevant snippet of the code. 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | id: expected-behavior 40 | attributes: 41 | label: Expected behavior 42 | description: >- 43 | What did you expect to happen when you encountered the bug? 44 | validations: 45 | required: true 46 | 47 | - type: textarea 48 | id: actual-behavior 49 | attributes: 50 | label: Actual Behavior 51 | description: >- 52 | What unexpected behavior actually occurred? Please include specific error messages, 53 | if relevant. 54 | validations: 55 | required: true 56 | 57 | - type: textarea 58 | id: notes 59 | attributes: 60 | label: Additional Notes 61 | description: >- 62 | Any additional information that you feel may be relevant to this bug report 63 | validations: 64 | required: false 65 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: true 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | description: Request an enhancement or new feature 4 | labels: 5 | - enhancement 6 | body: 7 | - type: textarea 8 | id: description 9 | attributes: 10 | label: Description 11 | description: >- 12 | An overview of the feature that you would like to see added, including a use case. It 13 | may be helpful to frame this as a 14 | [user story](https://www.atlassian.com/agile/project-management/user-stories), such as: 15 | 16 | > As a user, I want to [feature], so that [use case]. 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | id: acceptance 22 | attributes: 23 | label: Acceptance Criteria 24 | description: >- 25 | The conditions that must be met for this ticket to be accepted 26 | validations: 27 | required: false 28 | 29 | - type: textarea 30 | id: solution 31 | attributes: 32 | label: Proposed Solution 33 | description: >- 34 | If you have an idea for how this feature could be implemented, please provide that 35 | validations: 36 | required: false 37 | 38 | - type: textarea 39 | id: notes 40 | attributes: 41 | label: Additional Notes 42 | description: >- 43 | Any additional information that you feel may be relevant to this feature request 44 | validations: 45 | required: false 46 | 47 | - type: checkboxes 48 | id: implementation 49 | attributes: 50 | label: Implementation 51 | options: 52 | - label: I can (or plan to) submit a pull request to implement this 53 | - label: Implementing this may result in a breaking change 54 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | commit-message: 9 | include: scope 10 | prefix: "chore" 11 | target-branch: main 12 | reviewers: 13 | - "EasyDynamics/oscal-editor-reviewers" 14 | - package-ecosystem: github-actions 15 | directory: "/" 16 | schedule: 17 | interval: daily 18 | commit-message: 19 | include: scope 20 | prefix: "chore" 21 | target-branch: next 22 | reviewers: 23 | - "EasyDynamics/oscal-editor-reviewers" 24 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | changelog: 3 | exclude: 4 | labels: 5 | - chore 6 | - dependencies 7 | - spike 8 | categories: 9 | - title: New Features 10 | labels: 11 | - enhancement 12 | - title: Bug fixes 13 | labels: 14 | - bug 15 | -------------------------------------------------------------------------------- /.github/workflows/add-to-project.yml: -------------------------------------------------------------------------------- 1 | name: Adds all issues to the project board 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | env: 9 | PROJECT_ORG: "EasyDynamics" 10 | PROJECT_ID: "2" 11 | 12 | jobs: 13 | add-to-project: 14 | name: Add issue to project 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Login as the automation app 18 | # This Action generates a token from the GitHub App and provides it as 19 | # an output. It _does_ register that token as a secret so that it will be 20 | # filtered from log output automatically 21 | id: generate-token 22 | # This maps to v1.7.0 https://github.com/tibdex/github-app-token/releases/tag/v1.7.0 23 | uses: tibdex/github-app-token@0914d50df753bbc42180d982a6550f195390069f 24 | with: 25 | app_id: ${{ secrets.APP_ID }} 26 | private_key: ${{ secrets.APP_PRIVATE_KEY }} 27 | permissions: >- 28 | { 29 | "issues": "read", 30 | "organization_projects": "write", 31 | "metadata": "read" 32 | } 33 | - uses: actions/add-to-project@main 34 | with: 35 | project-url: https://github.com/orgs/${{ env.PROJECT_ORG }}/projects/${{ env.PROJECT_ID }} 36 | github-token: ${{ steps.generate-token.outputs.token }} 37 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "CodeQL" 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | - next 9 | pull_request: 10 | branches: 11 | - main 12 | - next 13 | schedule: 14 | - cron: '19 6 * * 4' 15 | 16 | jobs: 17 | analyze: 18 | name: Analyze 19 | runs-on: ubuntu-latest 20 | permissions: 21 | actions: read 22 | contents: read 23 | security-events: write 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | language: 28 | - javascript 29 | steps: 30 | - name: Checkout repository 31 | uses: actions/checkout@v4 32 | - name: Initialize CodeQL 33 | uses: github/codeql-action/init@v3 34 | with: 35 | languages: ${{ matrix.language }} 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v3 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v3 40 | -------------------------------------------------------------------------------- /.github/workflows/deploy-dev-viewer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Deploy viewer to Dev environment 3 | 4 | on: 5 | push: 6 | branches: ["main"] 7 | workflow_dispatch: 8 | schedule: 9 | # Run daily at 03:15 UTC (10:15/11:15 ET) 10 | - cron: "15 3 * * *" 11 | 12 | concurrency: 13 | group: "viewer-dev" 14 | cancel-in-progress: false 15 | 16 | jobs: 17 | build: 18 | name: Build site 19 | runs-on: ubuntu-latest 20 | defaults: 21 | run: 22 | shell: bash 23 | permissions: 24 | contents: read 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | with: 29 | fetch-depth: 0 30 | - name: Setup Node.js 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: 18 34 | cache: "npm" 35 | - name: Install global dependencies 36 | run: | 37 | npm install -g npm@latest 38 | - name: Install npm dependencies 39 | run: | 40 | npm ci 41 | - name: Build viewer 42 | run: 43 | npx lerna exec --include-dependencies --scope "@easydynamics/oscal-viewer" -- npm run build 44 | env: 45 | PUBLIC_URL: "/" 46 | - name: Archive viewer 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: oscal-viewer 50 | path: packages/oscal-viewer/build 51 | deploy: 52 | name: Deploy artifact 53 | needs: ["build"] 54 | runs-on: ubuntu-latest 55 | defaults: 56 | run: 57 | shell: bash 58 | permissions: 59 | contents: read 60 | id-token: write 61 | environment: 62 | name: development 63 | steps: 64 | - name: Download artifact 65 | uses: actions/download-artifact@v3 66 | with: 67 | path: oscal-viewer 68 | - name: Sign in to AWS 69 | uses: aws-actions/configure-aws-credentials@v4 70 | with: 71 | role-to-assume: "${{ secrets.ROLE_ARN }}" 72 | aws-region: "${{ vars.AWS_REGION }}" 73 | - name: Deploy files 74 | run: | 75 | aws s3 sync . s3://${{ secrets.S3_BUCKET_NAME }} --delete 76 | # The directory is doubled because of the trip through the GitHub artifact 77 | # storage. 78 | working-directory: oscal-viewer/oscal-viewer 79 | - name: Invalidate CloudFront cache 80 | run: 81 | aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/index.html" 82 | -------------------------------------------------------------------------------- /.github/workflows/deploy-prod-viewer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Deploy viewer to Production environment 3 | 4 | on: 5 | push: 6 | branches: 7 | - "main" 8 | workflow_dispatch: 9 | schedule: 10 | # Run daily at 03:15 UTC (10:15/11:15 ET) 11 | - cron: "15 3 * * *" 12 | 13 | concurrency: 14 | group: "viewer-prod" 15 | cancel-in-progress: false 16 | 17 | jobs: 18 | build: 19 | name: Build site 20 | runs-on: ubuntu-latest 21 | defaults: 22 | run: 23 | shell: bash 24 | permissions: 25 | contents: read 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: 18 35 | cache: "npm" 36 | - name: Install global dependencies 37 | run: | 38 | npm install -g npm@latest lerna@latest 39 | - name: Install npm dependencies 40 | run: | 41 | npm ci 42 | - name: Build viewer 43 | run: 44 | lerna exec --include-dependencies --scope "@easydynamics/oscal-viewer" -- npm run build 45 | env: 46 | REACT_APP_GOOGLE_ANALYTICS: "${{ secrets.GA4_CODE }}" 47 | PUBLIC_URL: "/" 48 | - name: Archive viewer 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: oscal-viewer 52 | path: packages/oscal-viewer/build 53 | deploy: 54 | name: Deploy artifact 55 | needs: ["build"] 56 | runs-on: ubuntu-latest 57 | defaults: 58 | run: 59 | shell: bash 60 | permissions: 61 | contents: read 62 | id-token: write 63 | environment: 64 | name: production 65 | url: https://viewer.oscal.io/ 66 | steps: 67 | - name: Download artifact 68 | uses: actions/download-artifact@v3 69 | with: 70 | path: oscal-viewer 71 | - name: Sign in to AWS 72 | uses: aws-actions/configure-aws-credentials@v4 73 | with: 74 | role-to-assume: "${{ secrets.ROLE_ARN }}" 75 | aws-region: "${{ vars.AWS_REGION }}" 76 | - name: Deploy files 77 | run: | 78 | aws s3 sync . s3://${{ secrets.S3_BUCKET_NAME }} --delete 79 | # The directory is doubled because of the trip through the GitHub artifact 80 | # storage. 81 | working-directory: oscal-viewer/oscal-viewer 82 | - name: Invalidate CloudFront cache 83 | run: 84 | aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/index.html" 85 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Publish Storybook 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | concurrency: 15 | group: "gh-pages" 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | build: 20 | name: Build Storybook site 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | - name: Setup NodeJS 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version: 18 29 | cache: "npm" 30 | - name: Setup pages 31 | uses: actions/configure-pages@v4 32 | - name: Globally update npm 33 | run: npm install -g npm@latest 34 | - name: Install dependencies 35 | run: npm ci 36 | - name: Build library's dependencies 37 | run: npx lerna exec --include-dependencies --scope '@easydynamics/oscal-react-library' -- npm run build 38 | - name: Build Storybook 39 | run: npm run build-storybook 40 | working-directory: packages/oscal-react-library 41 | - name: Upload artifact 42 | uses: actions/upload-pages-artifact@v2 43 | with: 44 | path: packages/oscal-react-library/storybook-static 45 | retention-days: "7" 46 | 47 | deploy: 48 | environment: 49 | name: storybook 50 | url: ${{ steps.deployment.outputs.page_url }} 51 | runs-on: ubuntu-latest 52 | needs: build 53 | steps: 54 | - name: Deploy to GitHub Pages 55 | id: deployment 56 | uses: actions/deploy-pages@v3 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # These are some examples of commonly ignored file patterns. 2 | # You should customize this list as applicable to your project. 3 | # Learn more about .gitignore: 4 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore 5 | 6 | # Node artifact files 7 | node_modules/ 8 | dist/ 9 | 10 | # Storybook files 11 | storybook-static/ 12 | 13 | # dependencies 14 | /node_modules 15 | /.pnp 16 | .pnp.js 17 | .eslintcache 18 | 19 | # builds 20 | build 21 | dist 22 | .rpt2_cache 23 | 24 | # misc 25 | .DS_Store 26 | .env 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | npm-debug.log* 33 | yarn-debug.log* 34 | yarn-error.log* 35 | 36 | # testing 37 | /coverage 38 | 39 | # production 40 | build/ 41 | 42 | # Compiled Java class files 43 | *.class 44 | 45 | # Compiled Python bytecode 46 | *.py[cod] 47 | 48 | # Log files 49 | *.log 50 | 51 | # Package files 52 | *.jar 53 | 54 | # Maven 55 | target/ 56 | dist/ 57 | 58 | # JetBrains IDE 59 | .idea/ 60 | 61 | # Eclipse 62 | .project 63 | 64 | # Unit test reports 65 | TEST*.xml 66 | 67 | # Generated by MacOS 68 | .DS_Store 69 | 70 | # Generated by Windows 71 | Thumbs.db 72 | 73 | # Applications 74 | *.app 75 | *.exe 76 | *.war 77 | 78 | # Large media files 79 | *.mp4 80 | *.tiff 81 | *.avi 82 | *.flv 83 | *.mov 84 | *.wmv 85 | example/.env.local 86 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Enable all rules by default 3 | default: true 4 | 5 | # Disable deprecated rules 6 | MD002: false 7 | MD006: false 8 | 9 | # Disable unwanted rules 10 | MD043: false 11 | 12 | # Rules for which we are overriding the default values 13 | 14 | MD003: 15 | # ATX style headers are the "#" or "##" at the start of a line 16 | style: "atx" 17 | 18 | MD013: 19 | # 120 characters should be readable in the vast majority of terminals 20 | line_length: 120 21 | code_block_line_length: 120 22 | stern: true 23 | 24 | MD035: 25 | style: "---" 26 | 27 | MD044: 28 | names: 29 | # These names have the potential to occur with incorrect capitalization 30 | # within documentation; we want to ensure that they are always capitalized 31 | # correctly. 32 | - "OSCAL" 33 | - "NIST" 34 | - "Easy Dynamics" 35 | # In code, these names are allowed to occur with incorrect capitalization 36 | code_blocks: false 37 | 38 | MD046: 39 | # Using fenced (```) for code blocks more clearly delineates the code from 40 | # the surrounding text. It is also easier to maintain and copy/paste 41 | style: "fenced" 42 | 43 | MD048: 44 | # While other characters may be able to be used to fence a code block, the 45 | # being consistent across the entire repository make is preferable and it is 46 | # unlikely that backticks will ever cause an issue compared to other allowed 47 | # fences. 48 | style: "backtick" 49 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": false, 4 | "trailingComma": "es5", 5 | "tabWidth": 2, 6 | "semi": true 7 | } 8 | -------------------------------------------------------------------------------- /.yaml-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ignore: | 3 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Easy Dynamics Corporation 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 0.2 3 | 4 | phases: 5 | install: 6 | runtime-versions: 7 | nodejs: 16 8 | commands: 9 | - echo "Update NPM" 10 | - npm install -g npm lerna 11 | - echo "Installing library npm packages" 12 | - npm ci 13 | build: 14 | commands: 15 | - echo "Build all packages" 16 | - lerna run build 17 | - echo "Testing library" 18 | - lerna run test --scope="@easydynamics/oscal-react-library" 19 | 20 | artifacts: 21 | base-directory: ./packages/oscal-viewer/build 22 | files: 23 | - '**/*' 24 | exclude-paths: 25 | - 'robots.txt' 26 | 27 | cache: 28 | paths: 29 | - './node_modules/**/*' 30 | - './packages/**/node_modules/**/*' 31 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "independent", 4 | "rejectCycles": true 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "scripts": { 5 | "lint": "lerna run lint", 6 | "build": "lerna run build", 7 | "test": "lerna run test" 8 | }, 9 | "workspaces": [ 10 | "packages/*" 11 | ], 12 | "devDependencies": { 13 | "@types/node": "^20.4.1", 14 | "eslint-plugin-prettier": "^5.0.0", 15 | "jest": "^29.6.1", 16 | "jest-environment-jsdom": "^29.6.1", 17 | "lerna": "^7.1.3", 18 | "quicktype-core": "^23.0.59", 19 | "ts-jest": "^29.1.1", 20 | "ts-node": "^10.9.1", 21 | "typescript": "^4.9.5" 22 | }, 23 | "dependencies": { 24 | "@fontsource/source-sans-pro": "^5.0.2" 25 | }, 26 | "proxy": "http://127.0.0.1:5001" 27 | } 28 | -------------------------------------------------------------------------------- /packages/oscal-react-library/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": ">0.2%, not dead, not op_mini all" 7 | } 8 | ], 9 | "@babel/preset-react" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/oscal-react-library/.eslintignore: -------------------------------------------------------------------------------- 1 | ## uncomment this to disable linting 2 | ##src/**/*.js 3 | 4 | build/ 5 | dist/ 6 | node_modules/ 7 | .snapshots/ 8 | *.min.js 9 | 10 | # These files must be CommonJS and won't meet the linting requirements 11 | rollup.config.js 12 | tests/fileTransform.js 13 | -------------------------------------------------------------------------------- /packages/oscal-react-library/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "parser": "@typescript-eslint/parser", 7 | "extends": [ 8 | "plugin:react/recommended", 9 | "plugin:json/recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:import/typescript", 12 | "prettier", 13 | "react-app", 14 | "react-app/jest" 15 | ], 16 | "parserOptions": { 17 | "ecmaFeatures": { 18 | "jsx": true 19 | }, 20 | "ecmaVersion": 12, 21 | "sourceType": "module", 22 | "requireConfigFile": false 23 | }, 24 | "plugins": [ 25 | "react", 26 | "prettier", 27 | "json" 28 | ], 29 | "settings": { 30 | "import/resolver": { 31 | "node": { 32 | "extensions": [ 33 | ".js", 34 | ".jsx", 35 | ".ts", 36 | ".tsx" 37 | ] 38 | } 39 | } 40 | }, 41 | "rules": { 42 | "react/jsx-filename-extension": [ 43 | 1, 44 | { 45 | "extensions": [ 46 | ".js", 47 | ".jsx", 48 | ".tsx" 49 | ] 50 | } 51 | ], 52 | "react/prop-types": [ 53 | 0 54 | ], 55 | "react/destructuring-assignment": [ 56 | 0 57 | ], 58 | "react/jsx-props-no-spreading": "off", 59 | "prettier/prettier": "error", 60 | "import/no-anonymous-default-export": "off", 61 | "import/no-extraneous-dependencies": [ 62 | "error", 63 | { 64 | "devDependencies": [ 65 | "**/*.test.js", 66 | "**/*.test.tsx", 67 | "**/setupTests.js", 68 | "**/TestUtils.js", 69 | "jest.config.ts", 70 | "rollup.config.js", 71 | "**/*.stories.tsx", 72 | "**/*.stories.ts", 73 | "**/*.stories.js" 74 | ] 75 | } 76 | ], 77 | "@typescript-eslint/no-unused-vars": [ 78 | "error", 79 | { 80 | "argsIgnorePattern": "^_", 81 | "varsIgnorePattern": "^_", 82 | "caughtErrorsIgnorePattern": "^_" 83 | } 84 | ], 85 | "@typescript-eslint/no-empty-function": "off" 86 | }, 87 | "overrides": [ 88 | { 89 | "files": [ 90 | "**/*.test.js", 91 | "**/*.test.jsx", 92 | "**/*.test.ts", 93 | "**/*.test.tsx", 94 | "**/test-data/**.js", 95 | "**/test-data/**.ts" 96 | ], 97 | "env": { 98 | "jest": true 99 | } 100 | } 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /packages/oscal-react-library/.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Increase allowed line length (to 100 characters) 2 | 51da849362be9c8ff6dfee0303ead0393d2304d6 3 | -------------------------------------------------------------------------------- /packages/oscal-react-library/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-webpack5'; 2 | 3 | export default { 4 | stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], 5 | addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-mdx-gfm', '@storybook/preset-create-react-app'], 6 | typescript: { 7 | check: true, 8 | checkOptions: {}, 9 | // reactDocgen: 'react-docgen-typescript', 10 | // reactDocgenTypescriptOptions: { 11 | // shouldExtractLiteralValuesFromEnum: true, 12 | // propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true), 13 | // }, 14 | }, 15 | framework: { 16 | name: '@storybook/react-webpack5', 17 | options: {} 18 | }, 19 | docs: { 20 | autodocs: true 21 | }, 22 | core: { 23 | disableTelemetry: true, 24 | }, 25 | } satisfies StorybookConfig; 26 | -------------------------------------------------------------------------------- /packages/oscal-react-library/createPalette.d.ts: -------------------------------------------------------------------------------- 1 | import "@mui/material/styles/createPalette"; 2 | 3 | declare module "@mui/material/styles/createPalette" { 4 | interface Palette { 5 | backgroundGray: PaletteColor; 6 | primaryAccent: PaletteColor; 7 | lightGray: PaletteColor; 8 | lightGrayBlue: PaletteColor; 9 | offWhite: PaletteColor; 10 | white: PaletteColor; 11 | black: PaletteColor; 12 | gray: PaletteColor; 13 | destructive: PaletteColor; 14 | lightPink: PaletteColor; 15 | lightGreen: PaletteColor; 16 | lightYellow: PaletteColor; 17 | lightBlue: PaletteColor; 18 | darkGray: PaletteColor; 19 | darkGray: PaletteColor; 20 | lightBlack: PaletteColor; 21 | lightMagenta: PaletteColor; 22 | darkBrown: PaletteColor; 23 | shadyGray: PaletteColor; 24 | shadyBlue: PaletteColor; 25 | smokyWhite: PaletteColor; 26 | darkWhite: PaletteColor; 27 | simpleBlue: PaletteColor; 28 | } 29 | interface PaletteOptions { 30 | backgroundGray?: PaletteColorOptions; 31 | primaryAccent?: PaletteColorOptions; 32 | lightGray?: PaletteColorOptions; 33 | lightGrayBlue?: PaletteColorOptions; 34 | offWhite?: PaletteColorOptions; 35 | white?: PaletteColorOptions; 36 | black?: PaletteColorOptions; 37 | gray?: PaletteColorOptions; 38 | destructive?: PaletteColorOptions; 39 | lightPink?: PaletteColorOptions; 40 | lightGreen?: PaletteColorOptions; 41 | lightYellow?: PaletteColorOptions; 42 | lightBlue?: PaletteColorOptions; 43 | darkGray?: PaletteColorOptions; 44 | lightBlack?: PaletteColorOptions; 45 | lightMagenta?: PaletteColorOptions; 46 | darkBrown?: PaletteColorOptions; 47 | shadyGray?: PaletteColorOptions; 48 | shadyBlue?: PaletteColorOptions; 49 | smokyWhite?: PaletteColorOptions; 50 | darkWhite?: PaletteColorOptions; 51 | simpleBlue?: PaletteColorOptions; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/oscal-react-library/custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | import React = require("react"); 3 | export const ReactComponent: React.FC>; 4 | const src: string; 5 | export default src; 6 | } 7 | 8 | declare module "*.png"; 9 | -------------------------------------------------------------------------------- /packages/oscal-react-library/docs/adding-anchor-links.md: -------------------------------------------------------------------------------- 1 | # Adding Anchor Links 2 | 3 | Anchor links provide value to users who wish to convenient navigate to elements 4 | on the page quickly from a link. When selecting an anchor link, a fragment 5 | (`#`) will appear in the browser search bar. When a link with a fragment is 6 | entered into the search bar the user will be navigated directly to the matching 7 | element on the page. 8 | 9 | Ideally, anchor links are added to important text elements on the page, 10 | including major headers and instances of items in lists, such as controls and 11 | back matter resources. 12 | 13 | ## General Usage 14 | 15 | Within an `oscal-react-library` component, in order to add an anchor link: 16 | 17 | 1. Navigate to the element/text 18 | 2. Surround the element/text within an `OSCALAnchorLinkHeader` component (which 19 | must be imported from `src/components/OSCALAnchorLinkHeader`) 20 | 3. (Optional) If you wish for the fragment to be a different value than the 21 | default value, specify a string with the `name` property 22 | - `name` by default is the embedded text within an `OSCALAnchorLinkHeader`, 23 | lower cased and hyphen separated 24 | 25 | ## A Basic Example 26 | 27 | Here's an example of a header with an `OSCALAnchorLinkHeader`: 28 | 29 | ```js 30 | 31 | Sample Section Header Text 32 | 33 | ``` 34 | 35 | When the anchor link icon is selected, `#sample-header` will 36 | appear at the end of the url. 37 | 38 | ## Other Important Notes 39 | 40 | Anchor links have been customized to allow styling within them, however keep 41 | the Anchor link around the core text and supported styling/component to avoid 42 | the anchor link appearing in the improper place or functioning incorrectly. 43 | 44 | While anchor links are typically thought to surround text section headers, 45 | anchor links can be added to buttons and other components as well, as long as 46 | text appears directly underneath the component or a value is provided. 47 | 48 | If the element for the anchor link isn't rendered at the initial page load, such 49 | as an item must be expanded/opened to reveal the element, the navigation to the 50 | element will not work properly. In this case, the fragment from the url must be 51 | passed to the component containing the element, and some state interaction must 52 | be carried out to reveal the element when a valid fragment is recognized 53 | (typically the fragment is a parameter in a `useEffect()`). 54 | -------------------------------------------------------------------------------- /packages/oscal-react-library/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "jest"; 2 | 3 | const config: Config = { 4 | roots: ["src"], 5 | moduleFileExtensions: ["ts", "tsx", "js", "jsx"], 6 | transform: { 7 | "^.+\\.[tj]sx?$": "ts-jest", 8 | // For non-code files, use the file transform. This will just return the name of the 9 | // file, matching the behavior for these files in `react-scripts`. 10 | "^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "/tests/fileTransform.js", 11 | }, 12 | testMatch: ["**/*.test.(ts|tsx|js|jsx)"], 13 | testEnvironment: "jsdom", 14 | transformIgnorePatterns: [ 15 | // Actually transform any given ESM modules, since Jest cannot support them 16 | "(?!(/node_modules/(react-markdown|vfile|vfile-message|unist-.*|unified|bail|is-plain-obj|trough|remark-.*|mdast-util-.*|micromark.*|decode-named-character-reference|character-entities|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|pretty-bytes|trim-lines)/))(/node_modules/.+.(js|jsx|mjs|cjs|ts|tsx)$)", 17 | ], 18 | setupFilesAfterEnv: ["/tests/setupTests.js"], 19 | }; 20 | 21 | export default config; 22 | -------------------------------------------------------------------------------- /packages/oscal-react-library/rollup.config.js: -------------------------------------------------------------------------------- 1 | import peerDepsExternal from "rollup-plugin-peer-deps-external"; 2 | import resolve from "@rollup/plugin-node-resolve"; 3 | import commonjs from "@rollup/plugin-commonjs"; 4 | import babel from "@rollup/plugin-babel"; 5 | import json from "@rollup/plugin-json"; 6 | import typescript from "rollup-plugin-typescript2"; 7 | import postcss from "rollup-plugin-postcss"; 8 | import url from '@rollup/plugin-url'; 9 | import svgr from '@svgr/rollup'; 10 | const packageJson = require("./package.json"); 11 | 12 | const EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"]; 13 | 14 | export default { 15 | input: ["src/index.ts"], 16 | output: [ 17 | { 18 | file: packageJson.main, 19 | format: "cjs", 20 | sourcemap: true, 21 | }, 22 | { 23 | file: packageJson.module, 24 | format: "esm", 25 | sourcemap: true, 26 | }, 27 | ], 28 | plugins: [ 29 | resolve({ preferBuiltins: false, extensions: EXTENSIONS }), 30 | peerDepsExternal(), 31 | postcss(), 32 | typescript({ 33 | tsconfigOverride: { 34 | exclude: ["**/stories.*"], 35 | }, 36 | }), 37 | json(), 38 | commonjs({ include: /node_modules/ }), 39 | babel({ 40 | babelHelpers: "bundled", 41 | extensions: EXTENSIONS, 42 | exclude: "**/node_modules/**", 43 | }), 44 | url(), 45 | svgr({ icon: true }), 46 | ], 47 | }; 48 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/common-tests/UrlParameter.test.js: -------------------------------------------------------------------------------- 1 | import { getRequestedUrl } from "../components/OSCALLoader"; 2 | import { urlParameterTestUrl } from "../test-data/Urls"; 3 | 4 | describe("Grabbing a URL Parameter from the Browser", () => { 5 | test("No Url Parameter Exists", () => { 6 | // The Url parameter should always initially be null. 7 | expect(getRequestedUrl()).toBeNull(); 8 | }); 9 | 10 | test("Url Parameter Exists", () => { 11 | const url = new URL(window.location); 12 | url.searchParams.set("url", urlParameterTestUrl); 13 | window.history.pushState({}, "", url); 14 | expect(getRequestedUrl()).toEqual(urlParameterTestUrl); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/ErrorHandling.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Alert from "@mui/material/Alert"; 3 | 4 | /** 5 | * A basic FallbackComponent for the {@link ErrorBoundary}. Displays a "yikes" 6 | * message using a Material UI Alert. 7 | */ 8 | export function BasicError(props: { error: Error }) { 9 | // Cleanup the message to prevent potential weird spacing around the parenthesis 10 | const message = props.error.message?.trim(); 11 | return ( 12 | 13 |
14 | Yikes! Something went wrong loading the OSCAL data. 15 | {message} 16 |
17 |
18 | ); 19 | } 20 | 21 | /** 22 | * A really horrible hacky solution to throw an error at the right part in a 23 | * tree. 24 | * 25 | * This allows an error to be thrown as a component in a tree at a particular 26 | * point. Using this is probably an indication that the components should be 27 | * restructured or that state should flow in a different way. 28 | * 29 | * @deprecated 30 | */ 31 | export function ErrorThrower(props: { error: Error }) { 32 | if (props.error) { 33 | throw props.error; 34 | } 35 | return null; 36 | } 37 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/HoverablePopover.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from "@testing-library/react"; 2 | import React from "react"; 3 | import { HoverablePopover } from "./HoverablePopover"; 4 | import userEvent from "@testing-library/user-event"; 5 | 6 | const hoverText = "Hover Text"; 7 | const popoverText = "Popover Text"; 8 | 9 | describe("HoverablePopover", () => { 10 | it("Displays text as children component", () => { 11 | render({hoverText}); 12 | 13 | const hover = screen.getByText(hoverText); 14 | 15 | expect(hover).toBeVisible(); 16 | }); 17 | 18 | it("Displays text as Popover component", async () => { 19 | render({hoverText}); 20 | 21 | const hover = screen.getByText(/hover text/i); 22 | userEvent.hover(hover); 23 | 24 | const popover = await screen.findByText(popoverText); 25 | expect(popover).toBeVisible(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/HoverablePopover.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Popover } from "@mui/material"; 2 | import React, { Fragment } from "react"; 3 | 4 | export interface HoverablePopoverProps { 5 | /** 6 | * The content of the popover. 7 | */ 8 | popoverContent: React.ReactNode; 9 | 10 | /** 11 | * Content to hover over. 12 | */ 13 | children: React.ReactNode; 14 | } 15 | 16 | export const HoverablePopover: React.FC = ({ popoverContent, children }) => { 17 | const [anchorEl, setAnchorEl] = React.useState(null); 18 | 19 | const handlePopoverOpen = (event: React.MouseEvent) => { 20 | setAnchorEl(event.currentTarget); 21 | }; 22 | 23 | const handlePopoverClose = () => { 24 | setAnchorEl(null); 25 | }; 26 | 27 | const open = Boolean(anchorEl); 28 | 29 | const popover = popoverContent ? ( 30 | 48 | {popoverContent} 49 | 50 | ) : null; 51 | 52 | return ( 53 | <> 54 | 61 | {children} 62 | 63 | {popover} 64 | 65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALAnchorLinkHeader.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, useState } from "react"; 2 | import Stack from "@mui/material/Stack"; 3 | import LinkIcon from "@mui/icons-material/Link"; 4 | import IconButton from "@mui/material/IconButton"; 5 | import Fade from "@mui/material/Fade"; 6 | import { styled } from "@mui/material/styles"; 7 | import { conformLinkIdText } from "./oscal-utils/OSCALLinkUtils"; 8 | 9 | const AnchorLinkIcon = styled(LinkIcon)(({ theme }) => ({ 10 | width: "0.75em", 11 | height: "0.75em", 12 | color: theme.palette.grey[500], 13 | })); 14 | 15 | export interface AnchorLinkProps { 16 | /** 17 | * he current fragment being handled 18 | */ 19 | readonly urlFragment?: string; 20 | /** 21 | * The beginning of a fragment which ends with the current group/control being handled 22 | */ 23 | readonly fragmentPrefix?: string; 24 | /** 25 | * The end of a fragment which starts with the current group/control being handled 26 | */ 27 | readonly fragmentSuffix?: string; 28 | } 29 | 30 | export interface OSCALAnchorLinkHeaderProps { 31 | /** 32 | * Text or element to link to. 33 | * 34 | * @example a 35 | */ 36 | children: ReactNode; 37 | /** 38 | * Value to be used in the id and fragment. If not specified, 39 | * children will be used. 40 | */ 41 | name?: string; 42 | } 43 | 44 | export const OSCALAnchorLinkHeader: React.FC = (props) => { 45 | const { children, name } = props; 46 | const [isHover, setIsHover] = useState(false); 47 | const onEnter = () => { 48 | setIsHover(true); 49 | }; 50 | const onLeave = () => { 51 | setIsHover(false); 52 | }; 53 | 54 | const linkId = name || conformLinkIdText(children); 55 | 56 | return ( 57 | 66 | {children} 67 | {isHover && ( 68 | // Navigate to a specified fragment or use the child text 69 | 70 | 71 | 72 | 73 | 74 | )} 75 | 76 | ); 77 | }; 78 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALCatalog.test.tsx: -------------------------------------------------------------------------------- 1 | import { Catalog, Control, ControlGroup } from "@easydynamics/oscal-types"; 2 | import { fireEvent, render, screen } from "@testing-library/react"; 3 | import React from "react"; 4 | import { metadataTestData } from "../test-data/CommonData"; 5 | import OSCALCatalog from "./OSCALCatalog"; 6 | 7 | const catalog: Catalog = { 8 | uuid: "a128d34d-6df8-4b86-8e7a-12e7e122a51b", 9 | metadata: metadataTestData, 10 | }; 11 | 12 | const groups: ControlGroup[] = [ 13 | { 14 | title: "Group 1", 15 | }, 16 | ]; 17 | 18 | const controls: Control[] = [ 19 | { 20 | id: "ac-1", 21 | title: "AC-1", 22 | }, 23 | { 24 | id: "ac-2", 25 | title: "AC-2", 26 | }, 27 | ]; 28 | 29 | const onResolutionComplete = () => {}; 30 | const parentUrl = "oscal"; 31 | 32 | describe("OSCALCatalogs", () => { 33 | test("displayed ungrouped controls without groups", () => { 34 | render( 35 | 40 | ); 41 | 42 | const controlTitle = screen.getByText(controls[0].title); 43 | 44 | expect(controlTitle).toBeVisible(); 45 | }); 46 | 47 | test("displayed ungrouped controls with groups", () => { 48 | render( 49 | 54 | ); 55 | 56 | const ungrouped = screen.getByText("Top"); 57 | fireEvent.click(ungrouped); 58 | 59 | const controlTitle = screen.getByText(controls[0].title); 60 | 61 | expect(ungrouped).toBeVisible(); 62 | expect(controlTitle).toBeVisible(); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALClass.tsx: -------------------------------------------------------------------------------- 1 | import { Chip } from "@mui/material"; 2 | import React from "react"; 3 | import { colorFromString } from "../utils"; 4 | 5 | interface HasClass { 6 | class?: string; 7 | } 8 | 9 | interface SmallInlineClassDisplayProps { 10 | item: HasClass; 11 | } 12 | 13 | export const SmallInlineClassDisplay: React.FC = ({ item }) => { 14 | return item.class ? ( 15 | 16 | ) : null; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALComponentDefinition.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import userEvent from "@testing-library/user-event"; 4 | import { OSCALComponentLoader } from "./OSCALLoader"; 5 | import OSCALComponentDefinition from "./OSCALComponentDefinition"; 6 | import { componentDefinitionTestData } from "../test-data/ComponentsData"; 7 | 8 | test("OSCALComponentDefinition loads", () => { 9 | render(); 10 | }); 11 | 12 | describe("OSCALComponentDefinition", () => { 13 | it("shows component title", () => { 14 | // GIVEN 15 | render( 16 | {}} 19 | /> 20 | ); 21 | 22 | // THEN 23 | expect(screen.getByText("Example Component")).toBeVisible(); 24 | }); 25 | 26 | it("shows component description", async () => { 27 | // GIVEN 28 | render( 29 | {}} 32 | /> 33 | ); 34 | 35 | // WHEN 36 | await userEvent.hover(screen.getByText("Example Component")); 37 | 38 | // THEN 39 | expect(await screen.findByText("An example component.")).toBeInTheDocument(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALComponentDefinitionControlImplementation.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Card from "@mui/material/Card"; 3 | import CardContent from "@mui/material/CardContent"; 4 | import Grid from "@mui/material/Grid"; 5 | import { List, ListItem, ListItemText } from "@mui/material"; 6 | import OSCALControlImplementationImplReq from "./OSCALControlImplementationImplReq"; 7 | import { OSCALSection, OSCALSectionHeader } from "./styles/CommonPageStyles"; 8 | import OSCALControlParamLegend from "./OSCALControlParamLegend"; 9 | import { OSCALAnchorLinkHeader } from "./OSCALAnchorLinkHeader"; 10 | 11 | export default function OSCALComponentDefinitionControlImplementation(props) { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | Control Implementations 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {props.controlImplementations.map((controlImpl) => ( 28 | 29 | 30 | {controlImpl.description} 31 | 32 | 33 | {controlImpl["implemented-requirements"].map((implementedRequirement) => ( 34 | 46 | ))} 47 | 48 | 49 | 50 | 51 | ))} 52 | 53 | 54 | 55 | 56 | 57 | 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALComponentDefinitionControlImplementation.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import OSCALComponentDefinitionControlImplementation from "./OSCALComponentDefinitionControlImplementation"; 4 | import getByTextIncludingChildren from "./oscal-utils/TestUtils"; 5 | import { controlsData } from "../test-data/ControlsData"; 6 | import { 7 | componentDefinitionControlImplementationTestData, 8 | componentDefinitionTestData, 9 | } from "../test-data/ComponentsData"; 10 | 11 | test("OSCALComponentDefinitionControlImplementation displays component implementation description", () => { 12 | render( 13 | 18 | ); 19 | const result = screen.getByText("This is an example description for control implementation-1"); 20 | expect(result).toBeVisible(); 21 | }); 22 | 23 | test("OSCALComponentDefinitionControlImplementation displays control ID", () => { 24 | render( 25 | 30 | ); 31 | const result = screen.getByText("control-1"); 32 | expect(result).toBeVisible(); 33 | }); 34 | 35 | test("OSCALComponentDefinitionControlImplementation displays component parameters in control prose", () => { 36 | render( 37 | 42 | ); 43 | const nonplaceholder1 = getByTextIncludingChildren(/Does something with/i); 44 | const placeholderText1 = getByTextIncludingChildren(/< control 1 \/ parameter 1 label >/i); 45 | const nonplaceholder2 = getByTextIncludingChildren(/and/i); 46 | const placeholderText2 = getByTextIncludingChildren(/< control 1 \/ parameter 2 label >/i); 47 | 48 | expect(nonplaceholder1).toBeVisible(); 49 | expect(placeholderText1).toBeVisible(); 50 | expect(nonplaceholder2).toBeVisible(); 51 | expect(placeholderText2).toBeVisible(); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALControl.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import OSCALControl from "./OSCALControl"; 4 | import OSCALControlImplementation from "./OSCALControlImplementation"; 5 | import { controlImplTestData, exampleControl } from "../test-data/ControlsData"; 6 | import { exampleComponents } from "../test-data/ComponentsData"; 7 | import { profileModifySmtTestData } from "../test-data/ModificationsData"; 8 | import { sspRestData } from "../test-data/SystemData"; 9 | 10 | test("OSCALCatalog displays controls", async () => { 11 | const testControl = { id: "ac-1" }; 12 | render(); 13 | const result = await screen.findByText("ac-1", { 14 | timeout: 30_000, 15 | }); 16 | expect(result).toBeVisible(); 17 | }); 18 | 19 | test("OSCALControl displays statement modifications", async () => { 20 | render( 21 | 28 | ); 29 | 30 | const modButton = await screen.findByRole( 31 | "button", 32 | { name: "control-1_smt modifications" }, 33 | { timeout: 10000 } 34 | ); 35 | fireEvent.click(modButton); 36 | 37 | expect(screen.getByText("Modifications")).toBeVisible(); 38 | expect(screen.getByText("Adds")).toBeVisible(); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALControlImplementationAdd.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { fireEvent, render, screen } from "@testing-library/react"; 3 | import { controlsData } from "../test-data/ControlsData"; 4 | import { sspRestData } from "../test-data/SystemData"; 5 | import OSCALControlImplementationAdd from "./OSCALControlImplementationAdd"; 6 | 7 | const controls = controlsData.map((control) => ({ 8 | id: control.id, 9 | title: control.title, 10 | })); 11 | const implementedControls = [controlsData[0].id]; 12 | 13 | describe("OSCALControlImplementationAdd", () => { 14 | test("displays the add button", () => { 15 | render( 16 | 21 | ); 22 | 23 | const addButton = screen.getByRole("button", { 24 | name: "add-system-security-plan-control-implementation", 25 | }); 26 | 27 | expect(addButton).toBeVisible(); 28 | }); 29 | 30 | test("displays autocomplete, save button, and cancel button on add button click", () => { 31 | render( 32 | 37 | ); 38 | 39 | const addButton = screen.getByRole("button", { 40 | name: "add-system-security-plan-control-implementation", 41 | }); 42 | fireEvent.click(addButton); 43 | 44 | const saveButton = screen.getByRole("button", { 45 | name: "save-system-security-plan-control-implementation", 46 | }); 47 | expect(saveButton).toBeVisible(); 48 | 49 | const cancelButton = screen.getByRole("button", { 50 | name: "cancel-system-security-plan-control-implementation", 51 | }); 52 | expect(cancelButton).toBeVisible(); 53 | 54 | const autocomplete = screen.getByRole("combobox", { 55 | name: "Select Control", 56 | }); 57 | expect(autocomplete).toBeVisible(); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALControlLabel.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { styled } from "@mui/material/styles"; 3 | import { Typography, TypographyProps } from "@mui/material"; 4 | 5 | const FallbackIdAsLabel = styled(Typography)(({ theme }) => ({ 6 | color: theme.palette.grey[400], 7 | textTransform: "uppercase", 8 | })) as typeof Typography; 9 | 10 | export interface OSCALControlLabelProps extends TypographyProps { 11 | label?: string; 12 | id?: string; 13 | children?: React.ReactNode; 14 | 15 | // Workaround for mui/material-ui#13921 16 | component: React.ElementType; 17 | } 18 | 19 | const OSCALControlLabel: React.FC = (props) => { 20 | const { label, id, children, ...rest } = props; 21 | if (label) { 22 | return ( 23 | 24 | {label} {children} 25 | 26 | ); 27 | } 28 | return ( 29 | 30 | {id} {children} 31 | 32 | ); 33 | }; 34 | export default OSCALControlLabel; 35 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALControlParamLegend.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { styled, useTheme } from "@mui/material/styles"; 3 | import { Box } from "@mui/material"; 4 | 5 | export default function OSCALControlParamLegend() { 6 | const LegendLabel = styled("div")( 7 | ({ theme }) => ` 8 | display: inline-flex; 9 | flex-direction: row; 10 | flex-wrap: wrap; 11 | justifyContent: flex-end; 12 | color: ${theme.palette.grey[400]}; 13 | font-size: 0.85em; 14 | padding-left: 1em; 15 | padding-right: 1em; 16 | padding-top: 0.5em; 17 | ` 18 | ); 19 | 20 | const legendBox = (color) => ( 21 | 30 | ); 31 | 32 | const theme = useTheme(); 33 | 34 | return ( 35 | <> 36 | 37 | {legendBox(theme.palette.warning.light)} 38 | Placeholder 39 | 40 | 41 | {legendBox(theme.palette.info.light)} 42 | Value 43 | 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALDiagram.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Typography from "@mui/material/Typography"; 3 | import resolveLinkHref from "./oscal-utils/OSCALLinkUtils"; 4 | 5 | export default function OSCALDiagram(props) { 6 | // Just grab the first rlink for now 7 | const link = props.diagram?.links[0]; 8 | if (!link) { 9 | throw new Error("no rlink found"); 10 | } 11 | 12 | let diagramUri; 13 | try { 14 | diagramUri = resolveLinkHref({ 15 | backMatter: props.backMatter, 16 | href: link.href, 17 | mediaType: /^image\//, 18 | parentUrl: props.parentUrl.startsWith(".") ? null : props.parentUrl, 19 | preferBase64: false, 20 | }); 21 | } catch (err) { 22 | // Silently fail on unresolved diagram resources 23 | diagramUri = link.href; 24 | } 25 | 26 | return ( 27 | <> 28 | {props.diagram.caption} 29 | 30 | {props.diagram.caption} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALDiagram.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import OSCALDiagram from "./OSCALDiagram"; 4 | import { backMatterTestData } from "../test-data/BackMatterData"; 5 | import { localReferenceDiagram, uriReferenceDiagram } from "../test-data/DiagramData"; 6 | 7 | function diagramRenderer(testData) { 8 | render( 9 | 14 | ); 15 | } 16 | 17 | export default function testOSCALDiagram(parentElementName, renderer, testData, expectedSrc) { 18 | test(`${parentElementName} displays diagram`, () => { 19 | renderer(testData); 20 | const result = screen.getByAltText("Authorization Boundary Diagram"); 21 | expect(result).toBeVisible(); 22 | expect(result).toHaveAttribute("src", expectedSrc); 23 | }); 24 | } 25 | 26 | if (!require.main) { 27 | testOSCALDiagram( 28 | "OSCALDiagram", 29 | diagramRenderer, 30 | localReferenceDiagram, 31 | "https://example.com/diagram.png" 32 | ); 33 | testOSCALDiagram( 34 | "OSCALDiagram", 35 | diagramRenderer, 36 | uriReferenceDiagram, 37 | uriReferenceDiagram.links[0].href 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALDrawerSelector.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import { OSCALDrawerSelector } from "./OSCALDrawerSelector"; 4 | 5 | describe("OSCALDrawerSelector", () => { 6 | test("loads", () => { 7 | render( {}} />); 8 | }); 9 | 10 | test(`displays expected tree items`, () => { 11 | render( {}} />); 12 | 13 | const catalogItem = screen.getByRole("treeitem", { 14 | name: "Catalogs", 15 | }); 16 | 17 | const componentItem = screen.getByRole("treeitem", { 18 | name: "Components", 19 | }); 20 | 21 | const profileItem = screen.getByRole("treeitem", { 22 | name: "Profiles", 23 | }); 24 | 25 | const sspItem = screen.getByRole("treeitem", { 26 | name: "System Security Plans", 27 | }); 28 | 29 | expect(catalogItem).toBeVisible(); 30 | expect(componentItem).toBeVisible(); 31 | expect(profileItem).toBeVisible(); 32 | expect(sspItem).toBeVisible(); 33 | }); 34 | 35 | test(`displays close button`, () => { 36 | render( {}} />); 37 | 38 | const closeButton = screen.getByTestId("ChevronLeftIcon"); 39 | 40 | expect(closeButton).toBeVisible(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALJsonEditor.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import { styled } from "@mui/material/styles"; 3 | import Button from "@mui/material/Button"; 4 | import Editor from "@monaco-editor/react"; 5 | import { Grid, Typography } from "@mui/material"; 6 | import SaveIcon from "@mui/icons-material/Save"; 7 | import CancelIcon from "@mui/icons-material/Cancel"; 8 | 9 | const BaseEditorGrid = styled(Grid)( 10 | ({ theme }) => ` 11 | padding-right: ${theme.spacing(1)}; 12 | top: ${theme.spacing(1)}; 13 | position: sticky; 14 | overflow: hidden 15 | ` 16 | ); 17 | 18 | const ButtonGrid = styled(Grid)( 19 | ({ theme }) => ` 20 | margin-top: ${theme.spacing(1)}; 21 | margin-bottom: ${theme.spacing(1)}; 22 | ` 23 | ); 24 | 25 | export default function OSCALJsonEditor(props) { 26 | const editorRef = useRef(props.editorRef); 27 | 28 | const editorOptions = { 29 | minimap: { 30 | enabled: false, 31 | }, 32 | scrollbar: { 33 | verticalHasArrows: true, 34 | }, 35 | tabSize: 2, 36 | }; 37 | 38 | return ( 39 | 40 | 41 | JSON Editor 42 | 43 | 44 | { 48 | editorRef.current = editor; 49 | }} 50 | value={props.value} 51 | defaultLanguage="json" 52 | /> 53 | 54 | 55 | 56 | 57 | 68 | 69 | 70 | 81 | 82 | 83 | 84 | 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALLoaderStyles.js: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | 3 | const OSCALDocumentRoot = styled("div")( 4 | ({ theme }) => ` 5 | padding-left: ${theme.spacing(2)}; 6 | display: flex; 7 | flex-direction: column; 8 | ` 9 | ); 10 | 11 | // eslint-disable-next-line import/prefer-default-export 12 | export { OSCALDocumentRoot }; 13 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALMarkupProse.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "@mui/material/Link"; 3 | import ReactMarkdown from "react-markdown"; 4 | 5 | import OpenInNewIcon from "@mui/icons-material/OpenInNew"; 6 | 7 | const baseComponents = { 8 | a: ({ ...props }) => 9 | props.href.startsWith("http") ? ( 10 | 11 | {props.children} 12 | 13 | ) : ( 14 | {props.children} 15 | ), 16 | }; 17 | 18 | export interface OSCALMarkupInterface { 19 | children: string; 20 | } 21 | 22 | export interface OSCALMarkupMultiLineInterface extends OSCALMarkupInterface { 23 | paragraphComponent?: any; 24 | } 25 | 26 | /** 27 | * Renders a string of markdown to React elements 28 | */ 29 | export const OSCALMarkupMultiLine: React.FC = (props) => { 30 | return ( 31 | 38 | ); 39 | }; 40 | 41 | export const OSCALMarkupLine: React.FC = (props) => { 42 | return ; 43 | }; 44 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALMetadataCommon.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | import { Typography } from "@mui/material"; 3 | 4 | /** 5 | * A collection of common components shared amongst 6 | * OSCALMetadata child components. 7 | */ 8 | 9 | export const OSCALMetadataLabel = styled(Typography)(({ theme }) => ({ 10 | textAlign: "right", 11 | color: theme.palette.text.secondary, 12 | })) as typeof Typography; 13 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALProfileCatalogInheritance.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ChevronRightIcon from "@mui/icons-material/ChevronRight"; 3 | import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; 4 | import { TreeItem, TreeView } from "@mui/lab"; 5 | import Card from "@mui/material/Card"; 6 | import Stack from "@mui/material/Stack"; 7 | import Chip from "@mui/material/Chip"; 8 | import Typography from "@mui/material/Typography"; 9 | import CardContent from "@mui/material/CardContent"; 10 | import { OSCALSection, OSCALSectionHeader } from "./styles/CommonPageStyles"; 11 | import { OSCALAnchorLinkHeader } from "./OSCALAnchorLinkHeader"; 12 | 13 | export default function OSCALProfileCatalogInheritance(props) { 14 | const documentLabel = (item) => ( 15 | 16 | {item.title} 17 | 18 | 19 | ); 20 | 21 | const inheritedListItem = (item) => ( 22 | 23 | {item.inherited?.map((inherited) => inheritedListItem(inherited))} 24 | 25 | ); 26 | return ( 27 | 28 | 29 | 30 | 31 | Profiles/Catalog Inheritance 32 | 33 | } 35 | defaultExpandIcon={} 36 | multiSelect 37 | > 38 | {props.inheritedProfilesAndCatalogs.inherited?.map((item) => inheritedListItem(item))} 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALProfileCatalogInheritance.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { fireEvent, render, screen } from "@testing-library/react"; 3 | import { profileCatalogInheritanceData } from "../test-data/CommonData"; 4 | import OSCALProfileCatalogInheritance from "./OSCALProfileCatalogInheritance"; 5 | 6 | describe("OSCALProfileCatalogInheritance", () => { 7 | test("displays top-level Catalog", () => { 8 | render( 9 | 12 | ); 13 | 14 | const resultCatalog = screen.getByText("Example Catalog"); 15 | expect(resultCatalog).toBeVisible(); 16 | }); 17 | 18 | test("displays top-level Profile", () => { 19 | render( 20 | 23 | ); 24 | 25 | const resultProfile = screen.getByText("Example Inherited Profile"); 26 | expect(resultProfile).toBeVisible(); 27 | }); 28 | 29 | test("displays Catalog imported from Profile", () => { 30 | render( 31 | 34 | ); 35 | 36 | const item = screen.getByText("Example Inherited Profile"); 37 | fireEvent.click(item); 38 | 39 | const nestedCatalog = screen.getByText("Nested Inherited Catalog"); 40 | expect(nestedCatalog).toBeVisible(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALProperties.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { fireEvent, render, screen } from "@testing-library/react"; 3 | import { OSCALPropertiesDialog } from "./OSCALProperties"; 4 | import { Property } from "@easydynamics/oscal-types"; 5 | import { NIST_DEFAULT_NAMESPACE } from "./oscal-utils/OSCALPropUtils"; 6 | 7 | const TEST_NAMESPACE = "http://easydynamics.com/ns/oscal/test"; 8 | const props: Property[] = [ 9 | { 10 | name: "Test Property", 11 | value: "Test Value", 12 | ns: TEST_NAMESPACE, 13 | }, 14 | { 15 | name: "marking", 16 | value: "test", 17 | ns: NIST_DEFAULT_NAMESPACE, 18 | }, 19 | { 20 | name: "status", 21 | value: "test", 22 | }, 23 | ]; 24 | 25 | const NAMESPACES = [TEST_NAMESPACE, NIST_DEFAULT_NAMESPACE]; 26 | 27 | describe("OSCAL Properties", () => { 28 | test("displays the property button", async () => { 29 | render(); 30 | expect(await screen.findByRole("button", { name: "Open Properties" })).toBeInTheDocument(); 31 | }); 32 | test("clicking the button opens the dialog", async () => { 33 | render(); 34 | const button = await screen.findByRole("button", { name: "Open Properties" }); 35 | fireEvent.click(button); 36 | expect(screen.getByText("Test Properties")).toBeVisible(); 37 | }); 38 | test.each(NAMESPACES)("namespace title appears (%s)", async (ns) => { 39 | render(); 40 | const button = await screen.findByRole("button", { name: "Open Properties" }); 41 | fireEvent.click(button); 42 | expect(screen.getByText(ns)).toBeVisible(); 43 | }); 44 | test.each(props.map(({ name }) => name))("property name appears (%s)", async (name) => { 45 | render(); 46 | const button = await screen.findByRole("button", { name: "Open Properties" }); 47 | fireEvent.click(button); 48 | expect(screen.getByText(name)).toBeVisible(); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALResponsibleRoles.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { styled } from "@mui/material/styles"; 3 | import Table from "@mui/material/Table"; 4 | import TableBody from "@mui/material/TableBody"; 5 | import TableCell from "@mui/material/TableCell"; 6 | import TableContainer from "@mui/material/TableContainer"; 7 | import TableRow from "@mui/material/TableRow"; 8 | 9 | const ResponsibleRolesTable = styled(TableContainer)( 10 | ({ theme }) => ` 11 | margin-top: ${theme.spacing(2)}; 12 | display: flex; 13 | flex-direction: column; 14 | text-transform: capitalize; 15 | ` 16 | ); 17 | 18 | export default function OSCALResponsibleRoles(props) { 19 | const getPartyName = (partyUuid) => 20 | props.parties?.find((party) => party.uuid === partyUuid)?.name; 21 | 22 | return ( 23 | 24 | 25 | 26 | {props.responsibleRoles && 27 | Object.entries(props.responsibleRoles).map(([key, role]) => ( 28 | 29 | 30 | {role["role-id"]} 31 | 32 | 33 | {role["party-uuids"] && 34 | role["party-uuids"].map((partyUuid) => getPartyName(partyUuid))} 35 | 36 | 37 | ))} 38 | 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALResponsibleRoles.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen, within } from "@testing-library/react"; 3 | import OSCALResponsibleRoles from "./OSCALResponsibleRoles"; 4 | import { metadataTestData, responsibleRolesTestData } from "../test-data/CommonData"; 5 | 6 | describe("OSCALResponsibleRoles", () => { 7 | test(`shows component roles`, () => { 8 | render( 9 | 13 | ); 14 | 15 | const responsibleRolesRow = screen.getByRole("row"); 16 | 17 | const rowHeader = within(responsibleRolesRow).getByRole("rowheader"); 18 | expect(rowHeader).toHaveTextContent("provider"); 19 | 20 | const rowData = within(responsibleRolesRow).getByText("Some group of people"); 21 | expect(rowData).toBeVisible(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSsp.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { OSCALSSPLoader } from "./OSCALLoader"; 4 | import OSCALSsp from "./OSCALSsp"; 5 | import testOSCALSystemCharacteristics from "./OSCALSystemCharacteristics.test"; 6 | import { sspTestData } from "../test-data/SystemData"; 7 | import testOSCALDiagram from "./OSCALDiagram.test"; 8 | 9 | test("OSCALSsp loads", () => { 10 | render(); 11 | }); 12 | 13 | function sspRenderer() { 14 | render( 15 | {}} /> 16 | ); 17 | } 18 | 19 | testOSCALSystemCharacteristics("OSCALSsp", sspRenderer); 20 | 21 | testOSCALDiagram("OSCALSsp", sspRenderer); 22 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALStyledTooltip.js: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | import Tooltip from "@mui/material/Tooltip"; 3 | 4 | /** 5 | * Creates a stylized tooltip with withStyles 6 | */ 7 | const StyledTooltip = styled(Tooltip)` 8 | tooltip { 9 | font-size: 1em; 10 | } 11 | `; 12 | 13 | export default StyledTooltip; 14 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSystemCharacteristics.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import OSCALSystemCharacteristics from "./OSCALSystemCharacteristics"; 4 | import { systemCharacteristicsTestData } from "../test-data/SystemData"; 5 | import { backMatterTestData } from "../test-data/BackMatterData"; 6 | 7 | function systemCharacteristicsRenderer() { 8 | render( 9 | 13 | ); 14 | } 15 | 16 | export default function testOSCALSystemCharacteristics(parentElementName, renderer) { 17 | test(`${parentElementName} shows system name`, () => { 18 | renderer(); 19 | const result = screen.getByRole("heading", { name: "Example System Name" }); 20 | expect(result).toBeVisible(); 21 | }); 22 | 23 | test(`${parentElementName} shows security sensitivity level`, () => { 24 | renderer(); 25 | const result = screen.getByLabelText("sensitivity-level"); 26 | expect(result).toHaveValue("moderate"); 27 | }); 28 | 29 | test(`${parentElementName} shows system information`, () => { 30 | renderer(); 31 | const result = screen.getByText("Information Type Title"); 32 | expect(result).toBeVisible(); 33 | }); 34 | 35 | test(`${parentElementName} shows security impact level`, () => { 36 | renderer(); 37 | 38 | const confidentialityResult = screen.getByLabelText("confidentiality"); 39 | expect(confidentialityResult).toHaveValue("confidentiality-value"); 40 | 41 | const integrityResult = screen.getByLabelText("integrity"); 42 | expect(integrityResult).toHaveValue("integrity-value"); 43 | 44 | const availabilityResult = screen.getByLabelText("availability"); 45 | expect(availabilityResult).toHaveValue("availability-value"); 46 | }); 47 | 48 | test(`${parentElementName} shows status`, () => { 49 | renderer(); 50 | const result = screen.getByLabelText("status"); 51 | expect(result).toHaveValue("other"); 52 | }); 53 | } 54 | if (!require.main) { 55 | testOSCALSystemCharacteristics("OSCALSystemCharacteristics", systemCharacteristicsRenderer); 56 | } 57 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSystemImplementation.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Card from "@mui/material/Card"; 3 | import CardContent from "@mui/material/CardContent"; 4 | import Grid from "@mui/material/Grid"; 5 | import Typography from "@mui/material/Typography"; 6 | import OSCALSystemImplementationUsers from "./OSCALSystemImplementationUsers"; 7 | import { OSCALSystemImplementationComponents } from "./OSCALSystemImplementationComponents"; 8 | import OSCALSystemImplementationInventoryItems from "./OSCALSystemImplementationInventoryItems"; 9 | import { OSCALSection, OSCALSectionHeader } from "./styles/CommonPageStyles"; 10 | import { OSCALMarkupMultiLine } from "./OSCALMarkupProse"; 11 | import { OSCALAnchorLinkHeader } from "./OSCALAnchorLinkHeader"; 12 | 13 | export default function OSCALSystemImplementation(props) { 14 | if (!props.systemImplementation) { 15 | return null; 16 | } 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | System Implementation 26 | 27 | 28 | 29 | 30 | {props.systemImplementation.remarks} 31 | 32 | 33 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSystemImplementation.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import { metadataTestData } from "../test-data/CommonData"; 4 | import { systemImplementationTestData } from "../test-data/SystemData"; 5 | import OSCALSystemImplementation from "./OSCALSystemImplementation"; 6 | 7 | describe("SystemImplementation", () => { 8 | test(`shows remarks`, () => { 9 | render( 10 | 14 | ); 15 | const result = screen.getByText("Example system implementation remarks."); 16 | expect(result).toBeVisible(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSystemImplementationInventoryItems.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen, within } from "@testing-library/react"; 3 | import { responsiblePartiesTestData } from "../test-data/CommonData"; 4 | import { inventoryItemsTestData, componentsTestData } from "../test-data/SystemData"; 5 | import OSCALSystemImplementationInventoryItems from "./OSCALSystemImplementationInventoryItems"; 6 | 7 | const setup = () => { 8 | render( 9 | 14 | ); 15 | }; 16 | 17 | describe("OSCALSystemImplementationInventoryItems", () => { 18 | test(`shows description`, () => { 19 | setup(); 20 | const result = screen.getByText("An example inventory item."); 21 | expect(result).toBeVisible(); 22 | }); 23 | 24 | test(`shows remarks`, () => { 25 | setup(); 26 | const result = screen.getByText("Additional information about this item."); 27 | expect(result).toBeVisible(); 28 | }); 29 | 30 | test(`shows prop name`, async () => { 31 | setup(); 32 | const result = screen.getByText("prop-1"); 33 | expect(result).toBeVisible(); 34 | }); 35 | 36 | test(`shows prop value`, async () => { 37 | setup(); 38 | const result = screen.getByText("An example property."); 39 | expect(result).toBeVisible(); 40 | }); 41 | 42 | test(`shows parties`, () => { 43 | setup(); 44 | const result = screen.getByText("provider"); 45 | expect(result).toBeVisible(); 46 | }); 47 | 48 | test(`shows implemented components name`, () => { 49 | setup(); 50 | const result = screen.getByText("Example Component"); 51 | expect(result).toBeVisible(); 52 | }); 53 | 54 | test(`shows implemented component type`, () => { 55 | setup(); 56 | 57 | // Grab example party row beneath heading row 58 | const exampleResponsiblePartiesRow = screen.getAllByRole("row")[1]; 59 | 60 | const component = within(exampleResponsiblePartiesRow).getByText("Example Component"); 61 | expect(component).toBeVisible(); 62 | 63 | const propNameResult = within(exampleResponsiblePartiesRow).getByText("software"); 64 | expect(propNameResult).toBeVisible(); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSystemImplementationPropertiesTable.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Table from "@mui/material/Table"; 3 | import TableBody from "@mui/material/TableBody"; 4 | import TableCell from "@mui/material/TableCell"; 5 | import TableContainer from "@mui/material/TableContainer"; 6 | import TableRow from "@mui/material/TableRow"; 7 | import { SmallTableCell } from "./OSCALSystemImplementationTableStyles"; 8 | 9 | export default function PropertiesTable(props) { 10 | return ( 11 | 12 | 13 | 14 | {props.list?.map((property) => ( 15 | 16 | 17 | {property.name} 18 | 19 | {property.value} 20 | 21 | ))} 22 | 23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSystemImplementationTableStyles.js: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | import Typography from "@mui/material/Typography"; 3 | import TableCell, { tableCellClasses } from "@mui/material/TableCell"; 4 | import TableRow from "@mui/material/TableRow"; 5 | import TableHead from "@mui/material/TableHead"; 6 | 7 | export const SmallTableCell = styled(TableCell)` 8 | text-align: right; 9 | padding: 0.75em 0.75em; 10 | `; 11 | 12 | export const ComponentTableCell = styled(TableCell)` 13 | text-align: left; 14 | minwidth: 20em; 15 | `; 16 | 17 | export const OSCALSystemImplementationTableTitle = styled(Typography)` 18 | flex: 1 1 100%; 19 | padding-top: 1.5em; 20 | padding-bottom: 1.5em; 21 | `; 22 | 23 | export const StyledHeaderTableCell = styled(TableCell)(({ theme }) => ({ 24 | [`&.${tableCellClasses.head}`]: { 25 | backgroundColor: theme.palette.primary.main, 26 | color: theme.palette.common.white, 27 | }, 28 | textAlign: "left", 29 | minWidth: "10em", 30 | })); 31 | 32 | export const StyledTableHead = styled(TableHead)` 33 | position: sticky; 34 | top: 0; 35 | `; 36 | 37 | export const StyledTableRow = styled(TableRow)(({ theme }) => ({ 38 | // Use hover color for even numbered rows 39 | "&:nth-of-type(even)": { 40 | backgroundColor: theme.palette.action.hover, 41 | }, 42 | })); 43 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/OSCALSystemImplementationUsers.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import { usersTestData } from "../test-data/SystemData"; 4 | import OSCALSystemImplementationUsers from "./OSCALSystemImplementationUsers"; 5 | 6 | describe("OSCALSystemImplementationUsers", () => { 7 | test("shows 'Users' section title", () => { 8 | render(); 9 | const result = screen.getByText("Users"); 10 | expect(result).toBeVisible(); 11 | }); 12 | 13 | test("shows 'Title' column", () => { 14 | render(); 15 | const result = screen.getByText("Title"); 16 | expect(result).toBeVisible(); 17 | }); 18 | 19 | test("shows 'Authorized Privileges' column", () => { 20 | render(); 21 | const result = screen.getByText("Authorized Privileges"); 22 | expect(result).toBeVisible(); 23 | }); 24 | 25 | test("shows name of a privilege listed", () => { 26 | render(); 27 | const result = screen.getByText("privilege title"); 28 | expect(result).toBeVisible(); 29 | }); 30 | 31 | test("shows name of a user listed", async () => { 32 | render(); 33 | fireEvent.mouseOver(screen.getByText("User 1")); 34 | expect(await screen.findByText("A system user")).toBeInTheDocument(); 35 | }); 36 | 37 | test("shows 'Authorized Privileges' title", () => { 38 | render(); 39 | const result = screen.getByText("privilege title"); 40 | expect(result).toBeVisible(); 41 | }); 42 | 43 | test("shows 'Authorized Privileges' description", async () => { 44 | render(); 45 | fireEvent.mouseOver(screen.getByText("privilege title")); 46 | expect(await screen.findByText("privilege description")).toBeInTheDocument(); 47 | }); 48 | 49 | test("shows read function preformed", () => { 50 | render(); 51 | const result = screen.getByText("reading function"); 52 | expect(result).toBeVisible(); 53 | }); 54 | 55 | test("shows write function preformed", () => { 56 | render(); 57 | const result = screen.getByText("writing function"); 58 | expect(result).toBeVisible(); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/EDC_Reverse_Tagline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-react-library/src/components/images/EDC_Reverse_Tagline.png -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/Uploading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/XinCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/blueCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/catalogs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/catalogs_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/component_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/dashboard_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/enterprise.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/enterprise_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/export.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/export_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/fileUpload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/filebucket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/gear.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/gear_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/greenCheck.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/greenCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/indent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/info_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/insert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/logout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/logout_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/notify.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/notify_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/orangeCircleChecked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/outdent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/plus_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/projects.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/projects_hov1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/projects_hov2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/quote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/redCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/redX.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/search_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/semiCircleOrange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/support.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/support_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/tools.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/tools_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/trash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/trash_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/images/icons/upload_hov.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/OSCALCatalogUtils.ts: -------------------------------------------------------------------------------- 1 | import type { Control, ControlGroup } from "@easydynamics/oscal-types"; 2 | import matchingProp from "./OSCALPropUtils"; 3 | 4 | /** 5 | * Determines if a control is withdrawn. 6 | * 7 | * @param control A catalog control 8 | * @returns true if control is withdrawn 9 | */ 10 | export default function isWithdrawn(item: Control | ControlGroup): boolean { 11 | // By default controls are *not* withdrawn 12 | if (!item.props) { 13 | return false; 14 | } 15 | return !!matchingProp(item.props, { name: "status", value: "withdrawn" }); 16 | } 17 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/OSCALComponentResolver.js: -------------------------------------------------------------------------------- 1 | import OSCALResolveProfileOrCatalogUrlControls from "./OSCALProfileResolver"; 2 | 3 | export default function OSCALComponentResolveSources( 4 | componentDefinition, 5 | parentUrl, 6 | onSuccess, 7 | onError 8 | ) { 9 | // Fixing linting error here would take significant change to codebase given how we use props. 10 | /* eslint no-param-reassign: "error" */ 11 | componentDefinition.resolvedControls = []; 12 | 13 | const inheritedProfilesAndCatalogs = { 14 | inherited: [], 15 | }; 16 | 17 | let pendingProcesses = componentDefinition.components 18 | .map((component) => component["control-implementations"]?.length ?? 0) 19 | .reduce((acc, length) => acc + length, 0); 20 | 21 | Object.entries(componentDefinition.components).forEach(([key, component]) => { 22 | component.uuid = key; 23 | component["control-implementations"].forEach((controlImplementation) => { 24 | controlImplementation.modifications = { 25 | "set-parameters": [], 26 | alters: [], 27 | }; 28 | OSCALResolveProfileOrCatalogUrlControls( 29 | componentDefinition.resolvedControls, 30 | controlImplementation.modifications, 31 | controlImplementation.source, 32 | parentUrl, 33 | componentDefinition["back-matter"], // not actually used for resolution in components 34 | inheritedProfilesAndCatalogs.inherited, 35 | () => { 36 | pendingProcesses -= 1; 37 | if (!pendingProcesses) { 38 | onSuccess(inheritedProfilesAndCatalogs); 39 | } 40 | }, 41 | () => { 42 | pendingProcesses -= 1; 43 | onError(); 44 | }, 45 | [] 46 | ); 47 | }); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/OSCALControlResolver.js: -------------------------------------------------------------------------------- 1 | export default function getControlOrSubControl(resolvedControls, controlId) { 2 | const findControlById = (controls, desiredId) => 3 | controls?.find((control) => control.id === desiredId); 4 | 5 | // Try to find control at root level of the resolved controls 6 | const control = findControlById(resolvedControls, controlId); 7 | if (control) { 8 | return control; 9 | } 10 | 11 | // If the control isn't at the root level, it may be a subcontrol of it's parent; 12 | // so for example, "ac-2.3" may be a child of "ac-2" 13 | const isSubControl = (testControlId) => testControlId.includes("."); 14 | if (isSubControl(controlId)) { 15 | const parentControlId = controlId.split(".")[0]; 16 | const parentControl = findControlById(resolvedControls, parentControlId); 17 | const subControl = findControlById(parentControl?.controls, controlId); 18 | 19 | if (subControl) { 20 | return subControl; 21 | } 22 | } 23 | 24 | // If the control isn't at root level or a subcontrol of it's parent, 25 | // it should be able to be found by just searching all subcontrols. 26 | return findControlById( 27 | resolvedControls?.flatMap((parentControl) => parentControl.controls), 28 | controlId 29 | ); 30 | } 31 | 32 | /** 33 | * Gets the 'by-component' object for the give componentId within the given 34 | * statementId from the given implReqStatements 35 | * 36 | * @see {@link https://pages.nist.gov/OSCAL/documentation/schema/implementation-layer/ssp/xml-schema/#global_by-component_h2} 37 | * @see {@link https://pages.nist.gov/OSCAL/documentation/schema/implementation-layer/ssp/xml-schema/#global_implemented-requirement} 38 | * 39 | * @param {object} implReqStatements Implementation Request Statements 40 | * @param {string} statementId Id of a statement 41 | * @param {string} componentId Id of a component 42 | * @returns Returns the by-component object when a statement is found 43 | */ 44 | export function getStatementByComponent(implReqStatements, statementId, componentId) { 45 | const foundStatement = implReqStatements?.find( 46 | (statement) => statement["statement-id"] === statementId 47 | ); 48 | // Error checking: Exit function when statement or it's by-components are not found 49 | if (!foundStatement || !foundStatement["by-components"]) { 50 | return null; 51 | } 52 | 53 | // Locate matching byComponent to componentId 54 | return foundStatement["by-components"].find( 55 | (byComponent) => byComponent["component-uuid"] === componentId 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/OSCALObjectData.ts: -------------------------------------------------------------------------------- 1 | import { Oscal } from "@easydynamics/oscal-types"; 2 | 3 | export type OscalObjectWrapped = Record>; 4 | 5 | export interface OscalObjectType { 6 | name: string; 7 | defaultUrl: string; 8 | defaultUuid: string; 9 | jsonRootName: keyof Oscal; 10 | restPath: string; 11 | } 12 | 13 | export const oscalObjectTypes: Record< 14 | "catalog" | "component" | "profile" | "ssp", 15 | OscalObjectType 16 | > = { 17 | catalog: { 18 | name: "Catalog", 19 | defaultUrl: 20 | "https://raw.githubusercontent.com/usnistgov/oscal-content/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_catalog.json", 21 | defaultUuid: "613fca2d-704a-42e7-8e2b-b206fb92b456", 22 | jsonRootName: "catalog", 23 | restPath: "catalogs", 24 | } satisfies OscalObjectType, 25 | component: { 26 | name: "Component", 27 | defaultUrl: 28 | "https://raw.githubusercontent.com/EasyDynamics/oscal-demo-content/main/component-definitions/example-component.json", 29 | defaultUuid: "8223d65f-57a9-4689-8f06-2a975ae2ad72", 30 | jsonRootName: "component-definition", 31 | restPath: "component-definitions", 32 | } satisfies OscalObjectType, 33 | profile: { 34 | name: "Profile", 35 | defaultUrl: 36 | "https://raw.githubusercontent.com/usnistgov/oscal-content/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json", 37 | defaultUuid: "8b3beca1-fcdc-43e0-aebb-ffc0a080c486", 38 | jsonRootName: "profile", 39 | restPath: "profiles", 40 | } satisfies OscalObjectType, 41 | ssp: { 42 | name: "System Security Plan", 43 | defaultUrl: 44 | "https://raw.githubusercontent.com/GSA/fedramp-automation/master/dist/content/rev4/templates/ssp/json/FedRAMP-SSP-OSCAL-Template.json", 45 | defaultUuid: "cff8385f-108e-40a5-8f7a-82f3dc0eaba8", 46 | jsonRootName: "system-security-plan", 47 | restPath: "system-security-plans", 48 | } satisfies OscalObjectType, 49 | } as const; 50 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/OSCALProfileUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Fixes source url if it does not properly link to a profile. 3 | * 4 | * @param {string} sourceURL url to fetch profile from 5 | * @param {string} parentUrl parent url to fix sourceUrl 6 | * @returns the correct source url 7 | */ 8 | export function fixProfileUrl(sourceUrl, parentUrl) { 9 | if (!sourceUrl?.startsWith("http")) { 10 | return `${parentUrl}/../${sourceUrl}`; 11 | } 12 | return sourceUrl; 13 | } 14 | 15 | /** 16 | * Fetches & sets the modifications from a profile at a given sourceURL. 17 | * 18 | * @param {string} sourceURL url to fetch profile from 19 | * @param {string} parentUrl parent url to fix sourceUrl 20 | * @param {function} setModifications set method for modifications 21 | */ 22 | export async function fetchProfileModifications(sourceUrl, parentUrl, setModifications) { 23 | const profileUrl = fixProfileUrl(sourceUrl, parentUrl); 24 | 25 | fetch(profileUrl) 26 | .then((res) => res.json()) 27 | .then( 28 | (result) => setModifications(result?.profile?.modify), 29 | () => null 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/OSCALPropUtils.test.ts: -------------------------------------------------------------------------------- 1 | import { NIST_DEFAULT_NAMESPACE, propWithName } from "./OSCALPropUtils"; 2 | 3 | const TEST_NS = "https://test.easydynamics.com/ns/oscal-react/"; 4 | 5 | const testProps = { 6 | nsWithStuff: [ 7 | { 8 | name: "test", 9 | value: "test2", 10 | ns: TEST_NS, 11 | }, 12 | { 13 | name: "test", 14 | value: "expect-this-value", 15 | }, 16 | ], 17 | noNs: [ 18 | { 19 | name: "test", 20 | value: "value", 21 | }, 22 | ], 23 | defaultNistNsExplicit: [ 24 | { 25 | name: "test", 26 | value: "test", 27 | // This is the default NIST OSCAL namespace from the documentation 28 | // directly. This allows us to ensure that the constant we use is 29 | // defined correctly. 30 | ns: "http://csrc.nist.gov/ns/oscal", 31 | }, 32 | ], 33 | }; 34 | 35 | describe("OSCAL Prop Utils", () => { 36 | it("gets correct value using default namespace", () => { 37 | // GIVEN 38 | const props = testProps.nsWithStuff; 39 | // THEN 40 | expect(propWithName(props, "test", TEST_NS)?.value).toBe("test2"); 41 | }); 42 | 43 | it("gets correct value when provided a namespace", () => { 44 | // GIVEN 45 | const props = testProps.nsWithStuff; 46 | // THEN 47 | expect(propWithName(props, "test", TEST_NS)?.value).toBe("test2"); 48 | }); 49 | 50 | it("has the correct value for the NIST namespace", () => { 51 | const props = testProps.defaultNistNsExplicit; 52 | expect(propWithName(props, "test", NIST_DEFAULT_NAMESPACE)?.value).toBe("test"); 53 | expect(propWithName(props, "test")?.value).toBe("test"); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/OSCALSspResolver.js: -------------------------------------------------------------------------------- 1 | import OSCALResolveProfileOrCatalogUrlControls from "./OSCALProfileResolver"; 2 | /** 3 | * Loads an SSP's 'import-profile' and adds the resulting controls to the SSP 4 | * 5 | * @see {@link https://pages.nist.gov/OSCAL/documentation/schema/implementation-layer/ssp/xml-schema/#global_import-profile} 6 | */ 7 | export default function OSCALSspResolveProfile(ssp, parentUrl, onSuccess, onError) { 8 | if (!ssp["import-profile"]) { 9 | return; 10 | } 11 | const profileUrl = ssp["import-profile"].href; 12 | 13 | const inheritedProfilesAndCatalogs = { 14 | inherited: [], 15 | }; 16 | 17 | // Fixing linting error here would take significant change to codebase given how we use props. 18 | /* eslint no-param-reassign: "error" */ 19 | ssp.resolvedControls = []; 20 | ssp.modifications = { 21 | "set-parameters": [], 22 | alters: [], 23 | }; 24 | 25 | OSCALResolveProfileOrCatalogUrlControls( 26 | ssp.resolvedControls, 27 | ssp.modifications, 28 | profileUrl, 29 | parentUrl, 30 | ssp["back-matter"], 31 | inheritedProfilesAndCatalogs.inherited, 32 | () => { 33 | onSuccess(inheritedProfilesAndCatalogs); 34 | }, 35 | onError, 36 | [] 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/oscal-utils/TestUtils.js: -------------------------------------------------------------------------------- 1 | import { screen } from "@testing-library/react"; 2 | 3 | function getByTextIncludingChildren(textMatch) { 4 | return screen.getByText((content, node) => { 5 | const hasText = (testNode) => 6 | testNode.textContent === textMatch || testNode.textContent.match(textMatch); 7 | const nodeHasText = hasText(node); 8 | const childrenDontHaveText = Array.from(node?.children || []).every((child) => !hasText(child)); 9 | return nodeHasText && childrenDontHaveText; 10 | }); 11 | } 12 | 13 | export default getByTextIncludingChildren; 14 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/styles/CommonPageStyles.js: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | import Typography from "@mui/material/Typography"; 3 | 4 | export const OSCALSectionHeader = styled(Typography)` 5 | font-size: 0.875rem; 6 | color: #0000008a; 7 | `; 8 | 9 | export const OSCALSection = styled("div")( 10 | ({ theme }) => ` 11 | margin-top: ${theme.spacing(2)}; 12 | display: flex; 13 | flex-direction: column; 14 | ` 15 | ); 16 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/styles/OSCALAccordion.tsx: -------------------------------------------------------------------------------- 1 | import ArrowForwardSharp from "@mui/icons-material/ArrowForwardIosSharp"; 2 | import MuiAccordion, { AccordionProps } from "@mui/material/Accordion"; 3 | import MuiAccordionDetails, { AccordionDetailsProps } from "@mui/material/AccordionDetails"; 4 | import MuiAccordionSummary, { AccordionSummaryProps } from "@mui/material/AccordionSummary"; 5 | import React from "react"; 6 | 7 | export const Accordion = (props: AccordionProps) => ( 8 | 9 | ); 10 | 11 | export const AccordionDetails = (props: AccordionDetailsProps) => ( 12 | 13 | ); 14 | 15 | export const AccordionSummary = (props: AccordionSummaryProps) => ( 16 | } {...props} /> 17 | ); 18 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/components/styles/OSCALAlerts.js: -------------------------------------------------------------------------------- 1 | import { Alert, Typography, styled } from "@mui/material"; 2 | import React from "react"; 3 | 4 | const ErrorAlert = styled(Alert)(({ theme }) => ({ 5 | borderLeft: `10px solid ${theme.palette.destructive.main}`, 6 | borderTop: `1px solid ${theme.palette.destructive.main}`, 7 | borderBottom: `1px solid ${theme.palette.destructive.main}`, 8 | borderRight: `1px solid ${theme.palette.destructive.main}`, 9 | backgroundColor: (theme) => theme.palette.lightPink.main, 10 | minWidth: "30rem", 11 | maxHeight: "17rem", 12 | })); 13 | 14 | export const OSCALError = (props) => { 15 | return ( 16 | 17 | {props.children} 18 | 19 | ); 20 | }; 21 | 22 | export const OSCALAlertError = (props) => { 23 | return ( 24 | `10px solid ${theme.palette.destructive.main}`, 29 | borderTop: (theme) => `1px solid ${theme.palette.destructive.main}`, 30 | borderBottom: (theme) => `1px solid ${theme.palette.destructive.main}`, 31 | borderRight: (theme) => `1px solid ${theme.palette.destructive.main}`, 32 | backgroundColor: (theme) => theme.palette.lightPink.main, 33 | minWidth: "30rem", 34 | maxHeight: "17rem", 35 | }} 36 | > 37 | theme.palette.destructive.main, 41 | textAlign: "left", 42 | }} 43 | > 44 | Errors 45 | 46 | {props.children} 47 | 48 | ); 49 | }; 50 | 51 | export const OSCALAlertWarning = (props) => { 52 | return ( 53 | `10px solid ${theme.palette.warning.main}`, 58 | borderTop: (theme) => `1px solid ${theme.palette.warning.main}`, 59 | borderBottom: (theme) => `1px solid ${theme.palette.warning.main}`, 60 | borderRight: (theme) => `1px solid ${theme.palette.warning.main}`, 61 | backgroundColor: (theme) => theme.palette.lightYellow.main, 62 | minWidth: "30rem", 63 | maxHeight: "17rem", 64 | }} 65 | > 66 | theme.palette.warning.main, 70 | textAlign: "left", 71 | }} 72 | > 73 | Warnings 74 | 75 | {props.children} 76 | 77 | ); 78 | }; 79 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OSCALCatalogLoader, 3 | OSCALSSPLoader, 4 | OSCALComponentLoader, 5 | OSCALProfileLoader, 6 | } from "./components/OSCALLoader"; 7 | import { OSCALDrawerSelector } from "./components/OSCALDrawerSelector"; 8 | import { OSCALPermanentDrawer } from "./components/OSCALPermanentDrawer"; 9 | import { OSCALAppBar } from "./components/OSCALAppBar"; 10 | import OSCALStyle from "./components/styles/OSCALStyle"; 11 | 12 | /** 13 | * Bundels loaders and referenced components into "dist/" when rollup is run. 14 | * Rollup.js is set to run with "npm start" in root directory. 15 | */ 16 | export { 17 | OSCALCatalogLoader, 18 | OSCALSSPLoader, 19 | OSCALComponentLoader, 20 | OSCALProfileLoader, 21 | OSCALDrawerSelector, 22 | OSCALPermanentDrawer, 23 | OSCALAppBar, 24 | OSCALStyle, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALBackMatter.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALBackMatter from "../components/OSCALBackMatter"; 3 | import { 4 | exampleBackMatter, 5 | exampleBackMatterWithCitation, 6 | exampleBackMatterWithDescription, 7 | exampleBackMatterWithMediaType, 8 | exampleBackMatterWithoutTitle, 9 | } from "../test-data/BackMatterData"; 10 | import { parentUrlTestData } from "../test-data/Urls"; 11 | 12 | export default { 13 | title: "Components/Back Matter", 14 | component: OSCALBackMatter, 15 | }; 16 | 17 | function Template(args) { 18 | return ; 19 | } 20 | 21 | export const Default = Template.bind({}); 22 | 23 | export const WithDescription = Template.bind({}); 24 | 25 | export const WithCitation = Template.bind({}); 26 | 27 | export const WithMediaType = Template.bind({}); 28 | 29 | export const WithoutTitle = Template.bind({}); 30 | 31 | export const WithType = Template.bind({}); 32 | 33 | export const WithTypeAndDescription = Template.bind({}); 34 | 35 | Default.args = { 36 | backMatter: exampleBackMatter, 37 | parentUrl: parentUrlTestData, 38 | }; 39 | 40 | WithDescription.args = { 41 | backMatter: exampleBackMatterWithDescription, 42 | parentUrl: parentUrlTestData, 43 | }; 44 | 45 | WithCitation.args = { 46 | backMatter: exampleBackMatterWithCitation, 47 | parentUrl: parentUrlTestData, 48 | }; 49 | 50 | WithMediaType.args = { 51 | backMatter: exampleBackMatterWithMediaType, 52 | parentUrl: parentUrlTestData, 53 | }; 54 | 55 | WithoutTitle.args = { 56 | backMatter: exampleBackMatterWithoutTitle, 57 | parentUrl: parentUrlTestData, 58 | }; 59 | 60 | WithType.args = { 61 | backMatter: { 62 | resources: [{ ...exampleBackMatter.resources[0], props: [{ name: "type", value: "logo" }] }], 63 | }, 64 | parentUrl: parentUrlTestData, 65 | }; 66 | 67 | WithTypeAndDescription.args = { 68 | backMatter: { 69 | resources: [ 70 | { 71 | ...exampleBackMatterWithDescription.resources[0], 72 | props: [{ name: "type", value: "logo" }], 73 | }, 74 | ], 75 | }, 76 | parentUrl: parentUrlTestData, 77 | }; 78 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALComponentDefinitionComponent.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALComponentDefinitionComponent from "../components/OSCALComponentDefinitionComponent"; 3 | import { exampleComponentDefinitionComponent } from "../test-data/ComponentsData"; 4 | import { exampleParties } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Component Definition Component", 8 | component: OSCALComponentDefinitionComponent, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | component: exampleComponentDefinitionComponent, 19 | parties: exampleParties, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALComponentDefinitionControlImplementation.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALComponentDefinitionControlImplementation from "../components/OSCALComponentDefinitionControlImplementation"; 3 | import { 4 | componentDefinitionControlImplementationTestData, 5 | exampleComponentStories, 6 | } from "../test-data/ComponentsData"; 7 | import { controlsData } from "../test-data/ControlsData"; 8 | 9 | export default { 10 | title: "Components/Component Definition Control Implementation", 11 | component: OSCALComponentDefinitionControlImplementation, 12 | }; 13 | 14 | function Template(args) { 15 | return ; 16 | } 17 | 18 | export const Default = Template.bind({}); 19 | 20 | Default.args = { 21 | controlImplementations: componentDefinitionControlImplementationTestData, 22 | components: exampleComponentStories, 23 | controls: controlsData, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALControl.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALControl from "../components/OSCALControl"; 3 | import { exampleControl } from "../test-data/ControlsData"; 4 | import { 5 | exampleModificationSetParameters, 6 | exampleModificationAltersTopLevel, 7 | } from "../test-data/ModificationsData"; 8 | 9 | export default { 10 | title: "Components/Control", 11 | component: OSCALControl, 12 | }; 13 | 14 | function Template(args) { 15 | return ; 16 | } 17 | 18 | export const Default = Template.bind({}); 19 | 20 | export const WithParameterConstraints = Template.bind({}); 21 | 22 | export const WithModifications = Template.bind({}); 23 | 24 | Default.args = { 25 | control: exampleControl, 26 | }; 27 | 28 | WithParameterConstraints.args = { 29 | control: exampleControl, 30 | modificationSetParameters: exampleModificationSetParameters, 31 | }; 32 | 33 | WithModifications.args = { 34 | control: exampleControl, 35 | modificationAlters: exampleModificationAltersTopLevel, 36 | }; 37 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALControlImplementation.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALControlImplementation from "../components/OSCALControlImplementation"; 3 | import { exampleComponents } from "../test-data/ComponentsData"; 4 | import { 5 | controlImplTestData, 6 | exampleControl, 7 | exampleImplNoStatements, 8 | } from "../test-data/ControlsData"; 9 | 10 | export default { 11 | title: "Components/Control Implementation", 12 | component: OSCALControlImplementation, 13 | }; 14 | 15 | function Template(args) { 16 | return ; 17 | } 18 | 19 | export const Default = Template.bind({}); 20 | 21 | export const WithImplementedStatements = Template.bind({}); 22 | 23 | const exampleControls = [exampleControl]; 24 | 25 | WithImplementedStatements.args = { 26 | controls: exampleControls, 27 | components: exampleComponents, 28 | controlImplementation: controlImplTestData, 29 | }; 30 | 31 | Default.args = { 32 | controls: exampleControls, 33 | components: exampleComponents, 34 | controlImplementation: exampleImplNoStatements, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALControlImplementationAdd.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALControlImplementationAdd from "../components/OSCALControlImplementationAdd"; 3 | import { controlsData } from "../test-data/ControlsData"; 4 | 5 | export default { 6 | title: "Components/ Oscal Control Implementation Add", 7 | component: OSCALControlImplementationAdd, 8 | }; 9 | 10 | function Template(args) { 11 | return ; 12 | } 13 | 14 | const controls = controlsData.map((control) => ({ 15 | id: control.id, 16 | title: control.title, 17 | })); 18 | 19 | const implementedControls = [controlsData[0].id]; 20 | 21 | export const Default = Template.bind({}); 22 | 23 | Default.args = { 24 | controls, 25 | implementedControls, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALControlModification.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALControlModification from "../components/OSCALControlModification"; 3 | import { exampleModificationAlters } from "../test-data/ModificationsData"; 4 | 5 | export default { 6 | title: "Components/Control Modification", 7 | component: OSCALControlModification, 8 | }; 9 | 10 | function Template(args) { 11 | return ; 12 | } 13 | 14 | export const Default = Template.bind({}); 15 | 16 | Default.args = { 17 | modificationAlters: exampleModificationAlters, 18 | controlId: "control-1", 19 | controlPartId: "control-1_smt.a", 20 | }; 21 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALControlPart.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALControlPart from "../components/OSCALControlPart"; 3 | import { exampleControl } from "../test-data/ControlsData"; 4 | import { 5 | exampleModificationAlters, 6 | exampleModificationSetParameters, 7 | } from "../test-data/ModificationsData"; 8 | 9 | export default { 10 | title: "Components/Control Part", 11 | component: OSCALControlPart, 12 | }; 13 | 14 | function Template(args) { 15 | return ; 16 | } 17 | 18 | export const Default = Template.bind({}); 19 | 20 | export const WithConstraints = Template.bind({}); 21 | 22 | export const WithModifications = Template.bind({}); 23 | 24 | WithConstraints.args = { 25 | control: exampleControl, 26 | part: exampleControl.parts[0], 27 | parameters: exampleControl.params, 28 | modificationSetParameters: exampleModificationSetParameters, 29 | componentId: "control-1_smt", 30 | }; 31 | 32 | WithModifications.args = { 33 | control: exampleControl, 34 | part: exampleControl.parts[0], 35 | parameters: exampleControl.params, 36 | modificationAlters: exampleModificationAlters, 37 | componentId: "control-1_smt", 38 | }; 39 | 40 | Default.args = { 41 | control: exampleControl, 42 | part: exampleControl.parts[0], 43 | parameters: exampleControl.params, 44 | componentId: "control-1_smt", 45 | }; 46 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALControlProse.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | OSCALReplacedProseWithByComponentParameterValue, 4 | OSCALReplacedProseWithParameterLabel, 5 | } from "../components/OSCALControlProse"; 6 | import { exampleModificationSetParameters } from "../test-data/ModificationsData"; 7 | import { controlProseTestData, exampleParams } from "../test-data/ControlsData"; 8 | import { exampleImplReqStatements } from "../test-data/ComponentsData"; 9 | 10 | export default { 11 | title: "Components/Control Prose", 12 | component: OSCALReplacedProseWithParameterLabel, 13 | }; 14 | 15 | function TemplateParameterLabel(args) { 16 | return ; 17 | } 18 | 19 | function TemplateByComponent(args) { 20 | return ; 21 | } 22 | 23 | export const ParametersReplacedWithLabels = TemplateParameterLabel.bind({}); 24 | 25 | export const ByComp = TemplateByComponent.bind({}); 26 | 27 | export const WithParameterConstraints = TemplateParameterLabel.bind({}); 28 | 29 | export const ByCompEdting = TemplateByComponent.bind({}); 30 | 31 | WithParameterConstraints.args = { 32 | modificationSetParameters: exampleModificationSetParameters, 33 | label: "a.", 34 | prose: controlProseTestData, 35 | parameters: exampleParams, 36 | }; 37 | 38 | ParametersReplacedWithLabels.args = { 39 | label: "a.", 40 | prose: controlProseTestData, 41 | parameters: exampleParams, 42 | }; 43 | 44 | ByComp.args = { 45 | label: "a.", 46 | prose: 47 | "Does something with {{ insert: param, control-1_prm_1 }} and {{ insert: param, control-1_prm_2 }}", 48 | parameters: exampleParams, 49 | statementId: "a_smt", 50 | componentId: "component-1", 51 | implReqStatements: exampleImplReqStatements, 52 | }; 53 | 54 | ByComp.storyName = "Parameters Replaced With By-Component Value"; 55 | 56 | ByCompEdting.args = { 57 | label: "a.", 58 | prose: 59 | "Does something with {{ insert: param, control-1_prm_1 }} and {{ insert: param, control-1_prm_2 }}", 60 | parameters: exampleParams, 61 | statementId: "a_smt", 62 | componentId: "component-1", 63 | implReqStatements: exampleImplReqStatements, 64 | isEditable: true, 65 | }; 66 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALDiagram.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALDiagram from "../components/OSCALDiagram"; 3 | import { backMatterTestData } from "../test-data/BackMatterData"; 4 | import { localReferenceDiagram } from "../test-data/DiagramData"; 5 | 6 | export default { 7 | title: "Components/Diagram", 8 | component: OSCALDiagram, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | diagram: localReferenceDiagram, 19 | backMatter: backMatterTestData, 20 | parentUrl: "./", 21 | }; 22 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALDocs.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from "@storybook/addon-docs"; 2 | 3 | 4 | 5 | # OSCAL React Library 6 | 7 | A library of React components that provides a direct view 8 | into NIST's Open Security Controls Assessment Language (OSCAL) data in JSON format. 9 | 10 | Follow this project and contribute on [GitHub.](https://github.com/EasyDynamics/oscal-react-library) 11 | 12 | ## Using Storybook 13 | 14 | This page displays [stories](https://storybook.js.org/docs/react/get-started/whats-a-story) for each isolated 15 | React component in the library. The components can be viewed and tested in various usage examples. 16 | Each story will display the props passed in for a given example at the bottom of the page, where they 17 | can be edited to test out different configurations. 18 | 19 | For more information on using Storybook, check out their official [documentation.](https://storybook.js.org/docs/react/get-started/introduction) 20 | 21 | ## Building 22 | 23 | Run `npm install` to install dependencies for the library. 24 | 25 | There is also an example OSCAL viewer to see the library in action; Check out the [live demo](https://oscal-viewer.msd.easydynamics.com/catalog) 26 | or build and run it locally with: `npm run build-library-and-run-example`. -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALEditableFieldActions.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALEditableFieldActions from "../components/OSCALEditableFieldActions"; 3 | 4 | export default { 5 | title: "Components/Modification Icons", 6 | component: OSCALEditableFieldActions, 7 | }; 8 | 9 | function Template(args) { 10 | return ; 11 | } 12 | 13 | export const Default = Template.bind({}); 14 | 15 | export const InEditMode = Template.bind({}); 16 | 17 | Default.args = { 18 | isEditable: true, 19 | modifiableData: { edit: [false, () => {}] }, 20 | editedField: ["storybook", "default"], 21 | }; 22 | 23 | InEditMode.args = { 24 | isEditable: true, 25 | modifiableData: { edit: [true, () => {}] }, 26 | editedField: ["storybook", "edit-mode"], 27 | }; 28 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALEditableTextField.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALEditableTextField from "../components/OSCALEditableTextField"; 3 | import { testModifiableMetadata, textFieldEditModeMetadata } from "../test-data/MetadataData"; 4 | 5 | export default { 6 | title: "Components/Editable Text Field", 7 | component: OSCALEditableTextField, 8 | }; 9 | 10 | function Template(args) { 11 | return ; 12 | } 13 | 14 | export const Default = Template.bind({}); 15 | 16 | export const InEditMode = Template.bind({}); 17 | 18 | Default.args = { 19 | modifiableData: testModifiableMetadata.title, 20 | }; 21 | 22 | InEditMode.args = { 23 | modifiableData: textFieldEditModeMetadata.title, 24 | textFieldSize: "medium", 25 | editedField: ["storybook", "editable-text-field", "edit-mode"], 26 | }; 27 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadata.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALMetadata from "../components/OSCALMetadata"; 3 | import { exampleMetadata, exampleMetadataWithPartiesAndRoles } from "../test-data/MetadataData"; 4 | 5 | export default { 6 | title: "Components/Metadata", 7 | component: OSCALMetadata, 8 | }; 9 | 10 | function Template(args) { 11 | return ; 12 | } 13 | 14 | export const Default = Template.bind({}); 15 | 16 | export const WithPartiesAndRoles = Template.bind({}); 17 | 18 | export const Editable = Template.bind({}); 19 | 20 | Default.args = { 21 | metadata: exampleMetadata, 22 | }; 23 | 24 | WithPartiesAndRoles.args = { 25 | metadata: exampleMetadataWithPartiesAndRoles, 26 | }; 27 | 28 | Editable.args = { 29 | metadata: exampleMetadata, 30 | isEditable: true, 31 | onFieldSave: () => {}, 32 | partialRestData: { "top-level": { uuid: "" } }, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataAddress.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataAddress } from "../components/OSCALMetadata"; 3 | 4 | import { exampleParties } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Party Address", 8 | component: OSCALMetadataAddress, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Home = Template.bind({}); 16 | 17 | export const Work = Template.bind({}); 18 | 19 | export const Unknown = Template.bind({}); 20 | 21 | Home.args = { 22 | address: exampleParties[0].addresses[0], 23 | }; 24 | 25 | Work.args = { 26 | address: exampleParties[0].addresses[1], 27 | }; 28 | 29 | Unknown.args = { 30 | address: exampleParties[0].addresses[2], 31 | }; 32 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataEmail.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataEmail } from "../components/OSCALMetadata"; 3 | 4 | import { exampleParties } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Party Email", 8 | component: OSCALMetadataEmail, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | email: exampleParties[0]["email-addresses"][0], 19 | }; 20 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataKeyword.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataKeyword } from "../components/OSCALMetadata"; 3 | 4 | import { keywordValuesList } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Keyword", 8 | component: OSCALMetadataKeyword, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | keyword: keywordValuesList.oneKeyWord[0].value, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataKeywords.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataKeywords } from "../components/OSCALMetadata"; 3 | 4 | import { keywordValuesList } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Keywords", 8 | component: OSCALMetadataKeywords, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | keywords: keywordValuesList.basic[0].value, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataLocation.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataLocation } from "../components/OSCALMetadata"; 3 | 4 | import { exampleLocations } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Location", 8 | component: OSCALMetadataLocation, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | location: exampleLocations[0], 19 | }; 20 | 21 | export const Minimum = Template.bind({}); 22 | 23 | Minimum.args = { 24 | location: exampleLocations[1], 25 | }; 26 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataLocationContent.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataLocationContent } from "../components/OSCALMetadata"; 3 | 4 | import { exampleLocations } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Location Content", 8 | component: OSCALMetadataLocationContent, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | location: exampleLocations[0], 19 | }; 20 | 21 | export const Minimum = Template.bind({}); 22 | 23 | Minimum.args = { 24 | location: exampleLocations[1], 25 | }; 26 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataParty.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataParty } from "../components/OSCALMetadata"; 3 | 4 | import { exampleParties } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Party", 8 | component: OSCALMetadataParty, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | party: exampleParties[0], 19 | partyRolesText: ["Example party role text"], 20 | }; 21 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALMetadataTelephone.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { OSCALMetadataTelephone } from "../components/OSCALMetadata"; 3 | 4 | import { exampleParties } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/Metadata Party Telephone", 8 | component: OSCALMetadataTelephone, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Mobile = Template.bind({}); 16 | 17 | export const Office = Template.bind({}); 18 | 19 | export const Unknown = Template.bind({}); 20 | 21 | export const Home = Template.bind({}); 22 | 23 | Mobile.args = { 24 | telephone: exampleParties[0]["telephone-numbers"][0], 25 | }; 26 | 27 | Office.args = { 28 | telephone: exampleParties[0]["telephone-numbers"][1], 29 | }; 30 | 31 | Unknown.args = { 32 | telephone: exampleParties[0]["telephone-numbers"][2], 33 | }; 34 | 35 | Home.args = { 36 | telephone: exampleParties[0]["telephone-numbers"][3], 37 | }; 38 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALProfileCatalogInheritance.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALProfileCatalogInheritance from "../components/OSCALProfileCatalogInheritance"; 3 | import { profileCatalogInheritanceData } from "../test-data/CommonData"; 4 | 5 | export default { 6 | title: "Components/Profile and Catalog Inheritance Display", 7 | component: OSCALProfileCatalogInheritance, 8 | }; 9 | 10 | function Template(args) { 11 | return ; 12 | } 13 | 14 | export const Default = Template.bind({}); 15 | 16 | Default.args = { 17 | inheritedProfilesAndCatalogs: profileCatalogInheritanceData, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALProperties.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StoryFn } from "@storybook/react"; 3 | import { Property } from "@easydynamics/oscal-types"; 4 | import { OSCALPropertiesDialog, OSCALPropertiesDialogProps } from "../components/OSCALProperties"; 5 | 6 | const exampleProperties: Property[] = [ 7 | { 8 | name: "Example Property 1", 9 | value: "Example Value", 10 | ns: "http://easydynamics.com/ns/oscal/example", 11 | }, 12 | { 13 | name: "Example Property 2", 14 | value: "Example Value", 15 | }, 16 | { 17 | name: "Example Property 3", 18 | value: "Example Value", 19 | }, 20 | ]; 21 | 22 | export default { 23 | title: "Components/Properties Dialog", 24 | component: OSCALPropertiesDialog, 25 | }; 26 | 27 | const Template: StoryFn = (args) => ; 28 | 29 | export const Default = Template.bind({}); 30 | 31 | Default.args = { 32 | properties: exampleProperties, 33 | title: "Components/Properties Dialog Title", 34 | }; 35 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALResponsibleRoles.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALResponsibleRoles from "../components/OSCALResponsibleRoles"; 3 | import { exampleParties, responsibleRolesTestData } from "../test-data/CommonData"; 4 | 5 | export default { 6 | title: "Components/Responsible Roles", 7 | component: OSCALResponsibleRoles, 8 | }; 9 | 10 | function Template(args) { 11 | return ; 12 | } 13 | 14 | export const Default = Template.bind({}); 15 | 16 | Default.args = { 17 | responsibleRoles: responsibleRolesTestData, 18 | parties: exampleParties, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALSystemCharacteristics.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALSystemCharacteristics from "../components/OSCALSystemCharacteristics"; 3 | import { systemCharacteristicsTestData } from "../test-data/SystemData"; 4 | import { backMatterTestData } from "../test-data/BackMatterData"; 5 | 6 | export default { 7 | title: "Components/System Characteristics", 8 | component: OSCALSystemCharacteristics, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | systemCharacteristics: systemCharacteristicsTestData, 19 | backMatter: backMatterTestData, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALSystemImplementation.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import OSCALSystemImplementation from "../components/OSCALSystemImplementation"; 3 | import { systemImplementationTestData, componentsTestData } from "../test-data/SystemData"; 4 | import { exampleParties } from "../test-data/CommonData"; 5 | 6 | export default { 7 | title: "Components/System Implementation", 8 | component: OSCALSystemImplementation, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | systemImplementation: systemImplementationTestData, 19 | parties: exampleParties, 20 | components: componentsTestData, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALSystemImplementationComponents.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { systemImplementationTestData } from "../test-data/SystemData"; 3 | import { exampleParties } from "../test-data/CommonData"; 4 | import { OSCALSystemImplementationComponents } from "../components/OSCALSystemImplementationComponents"; 5 | 6 | export default { 7 | title: "Components/System Implementation Components", 8 | component: OSCALSystemImplementationComponents, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | components: systemImplementationTestData.components, 19 | parties: exampleParties, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALSystemImplementationInventoryItems.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { systemImplementationTestData, componentsTestData } from "../test-data/SystemData"; 3 | import { exampleParties } from "../test-data/CommonData"; 4 | import OSCALSystemImplementationInventoryItems from "../components/OSCALSystemImplementationInventoryItems"; 5 | 6 | export default { 7 | title: "Components/System Implementation Inventory Items", 8 | component: OSCALSystemImplementationInventoryItems, 9 | }; 10 | 11 | function Template(args) { 12 | return ; 13 | } 14 | 15 | export const Default = Template.bind({}); 16 | 17 | Default.args = { 18 | inventoryItems: systemImplementationTestData["inventory-items"], 19 | parties: exampleParties, 20 | components: componentsTestData, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/stories/OSCALSystemImplementationUsers.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { systemImplementationTestData } from "../test-data/SystemData"; 3 | import OSCALSystemImplementationUsers from "../components/OSCALSystemImplementationUsers"; 4 | 5 | export default { 6 | title: "Components/System Implementation Users", 7 | component: OSCALSystemImplementationUsers, 8 | }; 9 | 10 | function Template(args) { 11 | return ; 12 | } 13 | 14 | export const Default = Template.bind({}); 15 | 16 | Default.args = { 17 | users: systemImplementationTestData.users, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/test-data/DiagramData.js: -------------------------------------------------------------------------------- 1 | export const localReferenceDiagram = { 2 | uuid: "bde155b3-6973-4bc7-9aa4-a4f6803968eb", 3 | description: "A diagram-specific explanation.", 4 | links: [ 5 | { 6 | href: "#a2fc103a-9fe6-433d-a83b-44a33db1564b", 7 | rel: "diagram", 8 | }, 9 | ], 10 | caption: "Authorization Boundary Diagram", 11 | }; 12 | 13 | export const uriReferenceDiagram = { 14 | uuid: "bde155b3-6973-4bc7-9aa4-a4f6803968eb", 15 | description: "A diagram-specific explanation.", 16 | links: [ 17 | { 18 | href: "https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/develop/example/public/logo192.png", 19 | rel: "diagram", 20 | }, 21 | ], 22 | caption: "Authorization Boundary Diagram", 23 | }; 24 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/test-data/MetadataData.js: -------------------------------------------------------------------------------- 1 | import { exampleParties } from "./CommonData"; 2 | 3 | const title = "Test Title"; 4 | const lastModified = "7/12/2021"; 5 | const version = "Revision 5"; 6 | const oscalVersion = "1.0.0"; 7 | 8 | export const exampleMetadata = { 9 | title, 10 | "last-modified": lastModified, 11 | version, 12 | "oscal-version": oscalVersion, 13 | }; 14 | 15 | export const exampleMetadataWithPartiesAndRoles = { 16 | title, 17 | "last-modified": lastModified, 18 | version, 19 | "oscal-version": oscalVersion, 20 | roles: [ 21 | { 22 | id: "creator", 23 | title: "Document creator", 24 | }, 25 | ], 26 | parties: exampleParties, 27 | "responsible-parties": [ 28 | { 29 | "role-id": "creator", 30 | "party-uuids": ["party-1"], 31 | }, 32 | ], 33 | }; 34 | 35 | export const testModifiableMetadata = { 36 | "last-modified": ["01-01-2022", () => {}], 37 | version: { 38 | ref: "TextField reference metadata version", 39 | edit: [false, () => {}], 40 | value: version, 41 | typographyVariant: "body2", 42 | }, 43 | title: { 44 | ref: "TextField reference metadata title", 45 | edit: [false, () => {}], 46 | value: title, 47 | typographyVariant: "h6", 48 | }, 49 | }; 50 | 51 | export const textFieldEditModeMetadata = { 52 | title: { 53 | ref: { 54 | current: "TextField reference metadata title", 55 | note: "Normally, would do React.useRef(value) for the ref property, this is just a placeholder.", 56 | }, 57 | edit: [true, () => {}], 58 | value: title, 59 | typographyVariant: "h6", 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/test-data/ModificationsData.js: -------------------------------------------------------------------------------- 1 | const props = [ 2 | { 3 | name: "Some prop name", 4 | value: "Some prop value", 5 | }, 6 | ]; 7 | 8 | export const exampleModificationSetParameters = [ 9 | { 10 | "param-id": "control-1_prm_1", 11 | constraints: [ 12 | { 13 | description: "some constraint", 14 | }, 15 | ], 16 | }, 17 | { 18 | "param-id": "control-1_prm_2", 19 | constraints: [ 20 | { 21 | description: "another constraint", 22 | }, 23 | ], 24 | }, 25 | { 26 | "param-id": "control-1_prm_3", 27 | values: ["param 3 value"], 28 | }, 29 | ]; 30 | 31 | export const exampleModificationSetParametersDecimal = [ 32 | { 33 | "param-id": "control-1.1_prm_1", 34 | constraints: [ 35 | { 36 | description: "some constraint", 37 | }, 38 | ], 39 | }, 40 | { 41 | "param-id": "control-1.1_prm_2", 42 | constraints: [ 43 | { 44 | description: "another constraint", 45 | }, 46 | ], 47 | }, 48 | { 49 | "param-id": "control-1.1_prm_3", 50 | values: ["param 3 value"], 51 | }, 52 | ]; 53 | 54 | export const exampleModificationAlters = [ 55 | { 56 | "control-id": "control-1", 57 | adds: [ 58 | { 59 | "by-id": "control-1_smt.a", 60 | position: "starting", 61 | props, 62 | }, 63 | ], 64 | }, 65 | ]; 66 | 67 | export const exampleModificationAltersTopLevel = [ 68 | { 69 | "control-id": "control-1", 70 | adds: [ 71 | { 72 | "by-id": "control-1", 73 | position: "starting", 74 | props, 75 | }, 76 | ], 77 | }, 78 | ]; 79 | 80 | const exampleModificationAltersSmt = [ 81 | { 82 | "control-id": "control-1", 83 | adds: [ 84 | { 85 | "by-id": "control-1_smt", 86 | position: "starting", 87 | props, 88 | }, 89 | ], 90 | }, 91 | ]; 92 | 93 | export const profileModifyTestData = { 94 | "set-parameters": exampleModificationSetParameters, 95 | alters: exampleModificationAltersTopLevel, 96 | }; 97 | 98 | export const profileModifyDecSmtTestData = { 99 | "set-parameters": exampleModificationSetParametersDecimal, 100 | alters: exampleModificationAltersTopLevel, 101 | }; 102 | 103 | export const profileModifySmtTestData = { 104 | "set-parameters": exampleModificationSetParameters, 105 | alters: exampleModificationAltersSmt, 106 | }; 107 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/test-data/Urls.js: -------------------------------------------------------------------------------- 1 | /* The following URLs are all of the URLs used in testing and in the stories. */ 2 | export const componentDefinitionUrl = 3 | "https://raw.githubusercontent.com/EasyDynamics/oscal-content/5ef5b3709045ba3aab1473813c13436e886d6185/examples/component-definition/json/example-component.json"; 4 | 5 | export const fedRampHighBaselineUrl = 6 | "https://raw.githubusercontent.com/usnistgov/oscal-content/master/fedramp.gov/json/FedRAMP_HIGH-baseline_profile.json"; 7 | 8 | export const parentUrlTestData = 9 | "https://raw.githubusercontent.com/usnistgov/oscal-content/master/nist.gov/SP800-53/rev4/json/NIST_SP-800-53_rev4_MODERATE-baseline_profile.json"; 10 | 11 | export const revFourCatalog = 12 | "https://raw.githubusercontent.com/usnistgov/oscal-content/master/nist.gov/SP800-53/rev4/json/NIST_SP-800-53_rev4_catalog.json"; 13 | 14 | export const rev4LowBaselineProfileJson = 15 | "https://raw.githubusercontent.com/usnistgov/oscal-content/master/nist.gov/SP800-53/rev4/json/NIST_SP-800-53_rev4_LOW-baseline_profile.json"; 16 | 17 | export const sspTestExampleUrl = 18 | "https://raw.githubusercontent.com/usnistgov/oscal-content/master/examples/ssp/json/oscal_leveraged-example_ssp.json"; 19 | 20 | export const systemCharacteristicsDescriptionUrl = "https://ietf.org/rfc/rfc4122"; 21 | 22 | export const systemCharacteristicsInformationUrl = "https://doi.org/10.6028/NIST.SP.800-60v2r1"; 23 | 24 | export const urlParameterTestUrl = "https://www.easydynamics.com/"; 25 | export const urlSmallCatalogUrl = 26 | "https://raw.githubusercontent.com/EasyDynamics/oscal-demo-content/test-content/catalogs/smallCatalog.json"; 27 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/test-data/resources/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-react-library/src/test-data/resources/diagram.png -------------------------------------------------------------------------------- /packages/oscal-react-library/src/utils.test.ts: -------------------------------------------------------------------------------- 1 | import * as util from "./utils"; 2 | 3 | describe("groupBy", () => { 4 | test("empty list yields an empty object", () => { 5 | expect(util.groupBy([], (item: any) => item.bar)).toEqual({}); 6 | }); 7 | test("items are grouped by key", () => { 8 | type Item = { count: number; name: string }; 9 | const items: Item[] = [ 10 | { count: 1, name: "a" }, 11 | { count: 1, name: "b" }, 12 | { count: 1, name: "c" }, 13 | { count: 2, name: "d" }, 14 | { count: 3, name: "e" }, 15 | { count: 3, name: "f" }, 16 | ]; 17 | expect(util.groupBy(items, (item) => item.count)).toEqual({ 18 | 1: [items[0], items[1], items[2]], 19 | 2: [items[3]], 20 | 3: [items[4], items[5]], 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/oscal-react-library/src/utils.ts: -------------------------------------------------------------------------------- 1 | export function groupBy(list: T[], key: (item: T) => K): Record { 2 | return list.reduce( 3 | (map, item) => { 4 | map[key(item)] ??= []; 5 | map[key(item)].push(item); 6 | return map; 7 | }, 8 | {} as Record 9 | ); 10 | } 11 | 12 | export const colorFromString = (name: string): string => { 13 | // This implementation hashes the given string to create a 14 | // color string. This is based of the example algorithm given 15 | // in the MUI documentation and adapted to our use case. 16 | let hash = 0; 17 | for (let i = 0; i < name.length; i += 1) { 18 | // eslint-disable-next-line no-bitwise 19 | hash = name.charCodeAt(i) + ((hash << 5) - hash); 20 | } 21 | 22 | let color = "#"; 23 | for (let i = 0; i < 3; i += 1) { 24 | // eslint-disable-next-line no-bitwise 25 | const value = (hash >> (i * 8)) & 0xff; 26 | color += `00${value.toString(16)}`.slice(-2); 27 | } 28 | // Add an alpha channe to prevent the color from being too dark 29 | return `${color}C0`; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/oscal-react-library/tests/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | process(sourceText, sourcePath, options) { 5 | return { 6 | code: `module.exports = ${JSON.stringify(path.basename(sourcePath))}`, 7 | }; 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/oscal-react-library/tests/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom"; 6 | 7 | if (window !== undefined) { 8 | import("whatwg-fetch"); 9 | } 10 | -------------------------------------------------------------------------------- /packages/oscal-react-library/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "declaration": true, 7 | "declarationDir": "lib", 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "react" 20 | }, 21 | "include": ["src", "custom.d.ts", "createPalette.d.ts"], 22 | "exclude": ["node_modules", "lib"], 23 | "outDir": "./dist" 24 | } 25 | -------------------------------------------------------------------------------- /packages/oscal-types/.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | -------------------------------------------------------------------------------- /packages/oscal-types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@easydynamics/oscal-types", 3 | "version": "0.1.0", 4 | "description": "Type definitions for the OSCAL specification", 5 | "main": "./lib/index.js", 6 | "types": "./lib/index.d.ts", 7 | "private": false, 8 | "scripts": { 9 | "build": "tsc", 10 | "watch": "npm run build -- --watch", 11 | "test": "echo 'This package has no tests'", 12 | "lint": "echo 'This package is not linted'" 13 | }, 14 | "files": [ 15 | "lib" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/EasyDynamics/oscal-react-library.git", 20 | "directory": "packages/oscal-types" 21 | }, 22 | "author": "Easy Dynamics", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/EasyDynamics/oscal-react-library/issues" 26 | }, 27 | "homepage": "https://github.com/EasyDynamics/oscal-react-library#readme", 28 | "proxy": "http://127.0.0.1:5001" 29 | } 30 | -------------------------------------------------------------------------------- /packages/oscal-types/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./oscal"; 2 | 3 | // Because of the type generation, some items get generated with "fun" names by the 4 | // `quicktype` library. Rather than modifying the `oscal.ts` file, they are exported 5 | // using better names. 6 | // It is possible that in the future, these may flip (or other "State"s will be added 7 | // causing these to change). We should keep an eye on this. Consumers of the library 8 | // should only consider the more serious-looking names to be stable. 9 | export { 10 | FluffyState as SystemCharacteristicStatusState, 11 | PurpleState as ComponentStatusState, 12 | } from "./oscal"; 13 | -------------------------------------------------------------------------------- /packages/oscal-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "declaration": true, 7 | "declarationDir": "lib", 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | }, 19 | "include": ["src"], 20 | "exclude": ["node_modules", "lib"], 21 | "outDir": "./dist" 22 | } 23 | -------------------------------------------------------------------------------- /packages/oscal-viewer/.eslintignore: -------------------------------------------------------------------------------- 1 | ## uncomment this to disable linting 2 | ##src/**/*.js 3 | 4 | build/ 5 | dist/ 6 | node_modules/ 7 | .snapshots/ 8 | *.min.js 9 | 10 | # These files must be CommonJS and won't meet the linting requirements 11 | rollup.config.js 12 | tests/fileTransform.js 13 | -------------------------------------------------------------------------------- /packages/oscal-viewer/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "parser": "@typescript-eslint/parser", 7 | "extends": [ 8 | "plugin:react/recommended", 9 | "plugin:json/recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:import/typescript", 12 | "prettier", 13 | "react-app", 14 | "react-app/jest" 15 | ], 16 | "parserOptions": { 17 | "ecmaFeatures": { 18 | "jsx": true 19 | }, 20 | "ecmaVersion": 12, 21 | "sourceType": "module", 22 | "requireConfigFile": false 23 | }, 24 | "plugins": [ 25 | "react", 26 | "prettier", 27 | "json" 28 | ], 29 | "settings": { 30 | "import/resolver": { 31 | "node": { 32 | "extensions": [ 33 | ".js", 34 | ".jsx", 35 | ".ts", 36 | ".tsx" 37 | ] 38 | } 39 | } 40 | }, 41 | "rules": { 42 | "react/jsx-filename-extension": [ 43 | 1, 44 | { 45 | "extensions": [ 46 | ".js", 47 | ".jsx", 48 | ".tsx" 49 | ] 50 | } 51 | ], 52 | "react/prop-types": [ 53 | 0 54 | ], 55 | "react/destructuring-assignment": [ 56 | 0 57 | ], 58 | "react/jsx-props-no-spreading": "off", 59 | "prettier/prettier": "error", 60 | "import/no-anonymous-default-export": "off", 61 | "import/no-extraneous-dependencies": [ 62 | "error", 63 | { 64 | "devDependencies": [ 65 | "**/*.test.js", 66 | "**/*.test.tsx", 67 | "**/setupTests.js", 68 | "**/TestUtils.js", 69 | "jest.config.ts", 70 | "rollup.config.js" 71 | ] 72 | } 73 | ], 74 | "@typescript-eslint/no-unused-vars": [ 75 | "error", 76 | { 77 | "argsIgnorePattern": "^_", 78 | "varsIgnorePattern": "^_", 79 | "caughtErrorsIgnorePattern": "^_" 80 | } 81 | ], 82 | "@typescript-eslint/no-empty-function": "off" 83 | }, 84 | "overrides": [ 85 | { 86 | "files": [ 87 | "**/*.test.js", 88 | "**/*.test.jsx", 89 | "**/*.test.ts", 90 | "**/*.test.tsx", 91 | "**/test-data/**.js", 92 | "**/test-data/**.ts" 93 | ], 94 | "env": { 95 | "jest": true 96 | } 97 | } 98 | ] 99 | } 100 | -------------------------------------------------------------------------------- /packages/oscal-viewer/README.md: -------------------------------------------------------------------------------- 1 | # Easy Dynamics OSCAL Viewer 2 | 3 | The OSCAL Viewer sample application is a React-based UI used for browsing OSCAL data. It is provided both as an 4 | example of using OSCAL React components and as a tool to view the complex OSCAL data concepts in an easily 5 | comprehensive format. 6 | 7 | An open sandbox environment of the project can be viewed at 8 | [https://viewer.oscal.io/catalog](https://viewer.oscal.io/catalog). 9 | 10 | ## Features 11 | 12 | In this initial iteration, the project renders basic elements of OSCAL catalogs, profiles, component 13 | definitions, and system security plans. 14 | 15 | The NIST 800-53 (rev 5) catalog is loaded by default in the catalog viewer: 16 | 17 | ![OSCSAL Catalog Viewer Screenshot](docs/resources/catalog-viewer-screenshot.png) 18 | 19 | The NIST 800-53 (rev 5) profile is loaded by default in the profile viewer: 20 | 21 | ![OSCSAL Profile Viewer Screenshot](docs/resources/profile-viewer-screenshot.png) 22 | 23 | An Easy Dynamics component definition is loaded by default in the component viewer: 24 | 25 | ![OSCSAL Component Viewer Screenshot](docs/resources/component-viewer-screenshot.png) 26 | 27 | The FedRAMP SSP template is loaded by default in the system security plan viewer: 28 | 29 | ![OSCSAL SSP Viewer Screenshot](docs/resources/ssp-viewer-screenshot.png) 30 | -------------------------------------------------------------------------------- /packages/oscal-viewer/craco.config.cjs: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-extraneous-dependencies 2 | const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin"); 3 | 4 | module.exports = { 5 | webpack: { 6 | configure(config) { 7 | const newConfig = config; 8 | const plugins = config.resolve.plugins.filter( 9 | (plugin) => !(plugin instanceof ModuleScopePlugin) 10 | ); 11 | newConfig.resolve.plugins = plugins; 12 | return newConfig; 13 | }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/oscal-viewer/docs/resources/catalog-viewer-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-viewer/docs/resources/catalog-viewer-screenshot.png -------------------------------------------------------------------------------- /packages/oscal-viewer/docs/resources/component-viewer-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-viewer/docs/resources/component-viewer-screenshot.png -------------------------------------------------------------------------------- /packages/oscal-viewer/docs/resources/profile-viewer-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-viewer/docs/resources/profile-viewer-screenshot.png -------------------------------------------------------------------------------- /packages/oscal-viewer/docs/resources/ssp-viewer-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-viewer/docs/resources/ssp-viewer-screenshot.png -------------------------------------------------------------------------------- /packages/oscal-viewer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@easydynamics/oscal-viewer", 3 | "homepage": ".", 4 | "version": "0.6.2-0", 5 | "private": true, 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/EasyDynamics/oscal-react-library.git", 9 | "directory": "packages/oscal-viewer" 10 | }, 11 | "scripts": { 12 | "start": "craco start", 13 | "build": "craco build", 14 | "watch": "craco start", 15 | "test": "craco test --watchAll=false", 16 | "eject": "craco eject", 17 | "lint": "eslint --ext .js,.jsx,.tsx,.ts,.json -c .eslintrc.json .", 18 | "lint:fix": "npm run lint -- --fix" 19 | }, 20 | "dependencies": { 21 | "@easydynamics/oscal-react-library": "^0.6.2-0", 22 | "@emotion/react": "^11.11.1", 23 | "@emotion/styled": "^11.11.0", 24 | "@fontsource/source-sans-pro": "^5.0.8", 25 | "@mui/icons-material": "^5.14.0", 26 | "@mui/lab": "^5.0.0-alpha.136", 27 | "@mui/material": "^5.14.0", 28 | "history": "^5.3.0", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "react-ga4": "^2.1.0", 32 | "react-markdown": "^8.0.7", 33 | "react-router-dom": "^6.14.1", 34 | "web-vitals": "^3.4.0" 35 | }, 36 | "devDependencies": { 37 | "@craco/craco": "^7.1.0", 38 | "@craco/types": "^7.1.0", 39 | "@testing-library/jest-dom": "^5.16.5", 40 | "@testing-library/react": "^14.0.0", 41 | "@testing-library/user-event": "^14.4.3", 42 | "@types/jest": "^29.5.3", 43 | "@types/react": "^18.2.14", 44 | "jest": "^29.6.1", 45 | "jest-environment-jsdom": "^29.6.1", 46 | "react-refresh": "^0.14.0", 47 | "ts-jest": "^29.1.1", 48 | "ts-node": "^10.9.1", 49 | "typescript": "^4.9.5" 50 | }, 51 | "browserslist": [ 52 | ">0.2%", 53 | "not dead", 54 | "not ie <= 11", 55 | "not op_mini all" 56 | ], 57 | "files": [ 58 | "build" 59 | ], 60 | "jest": { 61 | "transformIgnorePatterns": [ 62 | "(?!(/node_modules/(react-markdown|vfile|vfile-message|unist-.*|unified|bail|is-plain-obj|trough|remark-.*|mdast-util-.*|micromark.*|decode-named-character-reference|character-entities|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|pretty-bytes|trim-lines)/))(/node_modules/.+.(js|jsx|mjs|cjs|ts|tsx)$)", 63 | "^.+.module.(css|sass|scss)$" 64 | ] 65 | }, 66 | "proxy": "http://127.0.0.1:5001" 67 | } 68 | -------------------------------------------------------------------------------- /packages/oscal-viewer/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-viewer/public/favicon.ico -------------------------------------------------------------------------------- /packages/oscal-viewer/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | OSCAL Viewer 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /packages/oscal-viewer/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyDynamics/oscal-react-library/f79c221d93cb4696d67670ad61bf8bf1d26a019e/packages/oscal-viewer/public/logo192.png -------------------------------------------------------------------------------- /packages/oscal-viewer/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "OSCAL Viewer", 3 | "name": "Browse OSCAL data, including catalogs, profiles, component definitions, and system security plans", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo-header.svg", 17 | "type": "image/svg+xml" 18 | } 19 | ], 20 | "start_url": ".", 21 | "display": "standalone", 22 | "theme_color": "#001131", 23 | "background_color": "#ffffff" 24 | } 25 | -------------------------------------------------------------------------------- /packages/oscal-viewer/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: / 4 | -------------------------------------------------------------------------------- /packages/oscal-viewer/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MemoryRouter } from "react-router-dom"; 3 | import { render, screen } from "@testing-library/react"; 4 | import userEvent from "@testing-library/user-event"; 5 | import App from "./App"; 6 | 7 | test("renders menu navigation", async () => { 8 | render( 9 | 10 | 11 | 12 | ); 13 | 14 | await userEvent.click(screen.getByRole("button", { name: /menu/i })); 15 | const menuCVElement = screen.getByRole("menuitem", { 16 | name: /Catalog Viewer/i, 17 | }); 18 | const menuSSPElement = screen.getByRole("menuitem", { 19 | name: /System Security Plan Viewer/i, 20 | }); 21 | 22 | expect(menuCVElement).toBeVisible(); 23 | expect(menuSSPElement).toBeVisible(); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/oscal-viewer/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | import "@fontsource/source-sans-pro"; 7 | 8 | const root = document.getElementById("root"); 9 | if (!root) { 10 | throw new Error("No root element"); 11 | } 12 | createRoot(root).render( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals(); 24 | -------------------------------------------------------------------------------- /packages/oscal-viewer/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/oscal-viewer/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = (onPerfEntry) => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /packages/oscal-viewer/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom"; 6 | -------------------------------------------------------------------------------- /scripts/watch-viewer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | lerna="node_modules/.bin/lerna" 4 | 5 | if [ ! -f "$lerna" ]; then 6 | echo "Please run 'npm ci' before running this script" 7 | exit 1 8 | fi 9 | 10 | "$lerna" exec --stream --include-dependencies --scope="@easydynamics/oscal-viewer" -- npm run build 11 | "$lerna" exec --stream --include-dependencies --scope="@easydynamics/oscal-viewer" --parallel -- npm run watch 12 | --------------------------------------------------------------------------------