Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Syntax Highlighting, Example Hello World policy #67

Merged
merged 21 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a60d194
Fix issue where resource header was being overlapped by buttons and i…
pickjasmine Apr 27, 2021
36f61ec
Using react-simple-code-editor, seems to be working alright. Needs st…
pickjasmine Apr 27, 2021
1cda4cd
Syntax highlighting works with rego, added some colors for theme frie…
pickjasmine Apr 27, 2021
90b2660
Fix styling issues when error occurs
pickjasmine Apr 27, 2021
1e289fe
Tests around Code Editor component, fix onBlur test for policy form
pickjasmine Apr 27, 2021
23ac880
Linting, ignore prism file for linting and coverage
pickjasmine Apr 27, 2021
8222633
Licenses, remove comments
pickjasmine Apr 27, 2021
37f7e60
Use monospace wherever code shows, first pass at updating all code pl…
pickjasmine Apr 28, 2021
eadbed1
Use Code components wherever code is shown, fix tests broken by this …
pickjasmine Apr 28, 2021
ca62b7b
Test new code component
pickjasmine Apr 28, 2021
13feb44
Add example policy when creating policy
pickjasmine Apr 28, 2021
eb173f2
Remove unused styles, filter empty errors on validation
pickjasmine Apr 28, 2021
af15d19
Include prism in the docker build
pickjasmine Apr 28, 2021
46af9c2
fixing e2e test that was looking for an empty policy upon creation
pickjasmine Apr 28, 2021
7c5887a
Add license file to prism code, not sure if author should be rode?
pickjasmine Apr 28, 2021
bddba6b
Give credit where credit it due
pickjasmine Apr 28, 2021
189b9cb
Merge branch 'main' into rego-editor
alexashley Apr 28, 2021
7cc4511
Resolve PR comments
pickjasmine Apr 28, 2021
068a696
Add copyright back to get past checks, will revisit in the future
pickjasmine Apr 28, 2021
1ed48e7
Update colors for WCAG AA contrast in normal text
pickjasmine Apr 29, 2021
f3dd6cd
Move MIT Prism license to prism file
pickjasmine Apr 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
!hooks
!package.json
!pages
!prism
!providers
!public
!reducers
Expand Down
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ module.exports = {
rules: {
"no-restricted-imports": [2, { patterns: ["../*"] }],
},
ignorePatterns: ["prism/prism.js"],
};
43 changes: 43 additions & 0 deletions components/Code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright 2021 The Rode Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React, { useEffect } from "react";
import PropTypes from "prop-types";
import Prism from "prism/prism";
import styles from "styles/modules/Typography.module.scss";

// TODO: how to prevent non-highlighted code before highlight appears?
const Code = (props) => {
const { code, language, className = "", ...otherProps } = props;

useEffect(() => {
Prism.highlightAll();
}, []);
pickjasmine marked this conversation as resolved.
Show resolved Hide resolved

return (
<pre className={`${styles.codeContainer} ${className}`} {...otherProps}>
<code className={`language-${language}`}>{code}</code>
</pre>
);
};

Code.propTypes = {
code: PropTypes.string.isRequired,
language: PropTypes.oneOf(["rego", "json"]).isRequired,
className: PropTypes.string,
};

export default Code;
82 changes: 82 additions & 0 deletions components/CodeEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright 2021 The Rode Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React from "react";
import PropTypes from "prop-types";
import styles from "styles/modules/Inputs.module.scss";
import { useTheme } from "providers/theme";
import Editor from "react-simple-code-editor";
import Prism from "prism/prism";

const CodeEditor = (props) => {
const {
name,
label,
onChange,
placeholder,
value = "",
required = false,
error,
...otherProps
} = props;
const { theme } = useTheme();
const className = error ? styles.codeEditorError : styles.codeEditor;

return (
<div className={styles.outerWrapper}>
<div className={`${styles[theme]} ${styles.container}`}>
<label
htmlFor={name}
className={`${styles.label} ${required ? "required" : ""}`}
>
{label}
</label>
<Editor
name={name}
textareaId={name}
onValueChange={onChange}
placeholder={placeholder || label}
value={value}
tabSize={4}
className={className}
textareaClassName={className}
highlight={(code) => Prism.highlight(code, Prism.languages.rego)}
padding={16}
{...otherProps}
/>
</div>
{error && <p className={styles.errorMessage}>{error}</p>}
</div>
);
};

CodeEditor.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
placeholder: PropTypes.string,
value: PropTypes.string,
required: PropTypes.bool,
error: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
onChange: function (props) {
if (!props.disabled && !props.onChange) {
return new Error(
"The prop `onChange` is required in `CodeEditor` when the component is not disabled"
);
}
},
};

export default CodeEditor;
10 changes: 7 additions & 3 deletions components/occurrences/OccurrenceCodeModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Button from "components/Button";
import styles from "styles/modules/Modal.module.scss";
import Modal from "components/Modal";
import { copy } from "utils/shared-utils";
import Code from "components/Code";

const OccurrenceCodeModal = ({ json }) => {
const [showCode, setShowCode] = useState(false);
Expand All @@ -39,9 +40,12 @@ const OccurrenceCodeModal = ({ json }) => {
buttonType={"text"}
className={styles.copyButton}
/>
<pre data-testid="occurrenceJson" className={styles.jsonContainer}>
<code id={"occurrenceData"}>{stringifiedJson}</code>
</pre>
<Code
code={stringifiedJson}
language={"json"}
className={styles.jsonContainer}
data-testid={"occurrenceJson"}
/>
</Modal>
<Button onClick={() => setShowCode(true)} label={"Show JSON"} />
</>
Expand Down
10 changes: 7 additions & 3 deletions components/playground/EvaluationResult.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ICON_NAMES } from "utils/icon-utils";
import Button from "components/Button";
import Modal from "components/Modal";
import { copy } from "utils/shared-utils";
import Code from "components/Code";

const EvaluationResult = ({ results }) => {
const [showCode, setShowCode] = useState(false);
Expand Down Expand Up @@ -78,9 +79,12 @@ const EvaluationResult = ({ results }) => {
buttonType={"text"}
className={styles.copyButton}
/>
<pre data-testid="codeBlock" className={styles.explanationJson}>
<code>{formattedExplanation}</code>
</pre>
<Code
code={formattedExplanation}
language={"json"}
className={styles.explanationJson}
data-testid="codeBlock"
/>
</Modal>
</>
);
Expand Down
8 changes: 4 additions & 4 deletions components/policies/PolicyForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import Input from "components/Input";
import TextArea from "components/TextArea";
import Button from "components/Button";
import styles from "styles/modules/Policy.module.scss";
import { useTheme } from "providers/theme";
Expand All @@ -30,6 +29,7 @@ import { usePolicies } from "providers/policies";
import { policyActions } from "reducers/policies";
import Modal from "components/Modal";
import PageHeader from "components/layout/PageHeader";
import CodeEditor from "components/CodeEditor";

const PolicyForm = ({
title,
Expand Down Expand Up @@ -187,13 +187,13 @@ const PolicyForm = ({
horizontal
onBlur={validateField}
/>
<TextArea
<CodeEditor
name={"regoContent"}
label={"Rego Policy Code"}
value={regoContent}
onChange={(event) => {
onChange={(value) => {
setValidationResults(null);
setRegoContent(event.target.value);
setRegoContent(value);
}}
error={errors.regoContent || validationResults?.isValid === false}
required
Expand Down
8 changes: 5 additions & 3 deletions components/policies/PolicyValidationResult.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import PropTypes from "prop-types";
import styles from "styles/modules/Policy.module.scss";
import { ICON_NAMES } from "utils/icon-utils";
import Icon from "components/Icon";
import Code from "components/Code";

const PolicyValidationResult = ({ validation }) => {
if (!validation) {
Expand All @@ -39,9 +40,10 @@ const PolicyValidationResult = ({ validation }) => {
<p>This policy failed validation.</p>
</div>
{validation.errors?.length ? (
<pre>
<code>{validation.errors}</code>
</pre>
<Code
code={validation.errors.filter((error) => error.length)}
language={"json"}
/>
) : null}
</div>
)}
Expand Down
21 changes: 13 additions & 8 deletions cypress/integration/Policy.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,22 @@ Feature: Policies
When I create the "New" policy
Then I see "New" policy details

Scenario Outline: Create policy - required fields
Scenario: Create policy - require name field
Given I am on the "CreatePolicy" page
When I click the "SavePolicy" button
Then I see "<message>" message
When I type "<text>" into "<input>" input
Then I see "PolicyNameRequired" message
When I type "name" into "PolicyName" input
And I click the "SavePolicy" button
Then I no longer see "<message>" message
Scenarios:
| message | text | input |
| PolicyNameRequired | name | PolicyName |
| PolicyRegoRequired | rego | PolicyRegoContent |
Then I no longer see "PolicyNameRequired" message

Scenario: Create policy - require rego content field
Given I am on the "CreatePolicy" page
When I clear the "PolicyRegoContent" input
When I click the "SavePolicy" button
Then I see "PolicyRegoRequired" message
When I type "text" into "PolicyRegoContent" input
And I click the "SavePolicy" button
Then I no longer see "PolicyRegoRequired" message

Scenario Outline: Create policy - validating rego code
Given I am on the "CreatePolicy" page
Expand Down
5 changes: 5 additions & 0 deletions cypress/integration/common/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ When(/^I click the "([^"]*)" button$/, (buttonName) => {
cy.get(selectors[button]).click();
});

When(/^I clear the "([^"]*)" input$/, (inputName) => {
const input = `${inputName}Input`;
cy.get(selectors[input]).clear();
});

When(/^I type "([^"]*)" into "([^"]*)" input$/, (text, inputName) => {
const input = `${inputName}Input`;
cy.get(selectors[input]).type(text);
Expand Down
4 changes: 4 additions & 0 deletions jest-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import "@testing-library/jest-dom";
import "jest-chain";
import Chance from "chance";
import Prism from "prism/prism";

// eslint-disable-next-line no-unused-vars
const chance = new Chance();
Expand All @@ -25,3 +26,6 @@ const chance = new Chance();
console.error = jest.fn();

window.matchMedia = () => ({ matches: false });

jest.mock("prism/prism");
Prism.highlightAll = jest.fn();
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"prop-types": "^15.7.2",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-simple-code-editor": "^0.11.0",
"react-toastify": "^7.0.3",
"sass": "^1.32.7",
"swr": "^0.4.2",
Expand Down Expand Up @@ -56,7 +57,8 @@
"!**/.jest/**",
"!**test/**",
"!coverage/**",
"!cypress/**"
"!cypress/**",
"!prism/prism.js"
],
"moduleNameMapper": {
"\\.(css|scss|sass)$": "identity-obj-proxy"
Expand Down
10 changes: 4 additions & 6 deletions pages/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

import React, { useState, useEffect } from "react";
import TextArea from "components/TextArea";
import Button from "components/Button";
import styles from "styles/modules/Playground.module.scss";
import { useTheme } from "providers/theme";
Expand All @@ -28,6 +27,7 @@ import { policyActions } from "reducers/policies";
import { useResources } from "providers/resources";
import { resourceActions } from "reducers/resources";
import PageHeader from "components/layout/PageHeader";
import Code from "components/Code";

const PolicyPlayground = () => {
const { theme } = useTheme();
Expand Down Expand Up @@ -173,11 +173,9 @@ const PolicyPlayground = () => {
<span className={styles.label}>Description</span>
<span>{state.evaluationPolicy.description}</span>
</p>
<TextArea
name={"regoContent"}
label={"Rego Policy Code"}
disabled
value={state.evaluationPolicy.regoContent}
<Code
code={state.evaluationPolicy.regoContent}
language={"rego"}
/>
<Button
buttonType={"textDestructive"}
Expand Down
5 changes: 2 additions & 3 deletions pages/policies/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { usePolicy } from "hooks/usePolicy";
import { usePolicies } from "providers/policies";
import { policyActions } from "reducers/policies";
import PageHeader from "components/layout/PageHeader";
import Code from "components/Code";

const Policy = () => {
const router = useRouter();
Expand Down Expand Up @@ -75,9 +76,7 @@ const Policy = () => {
/>
<div className={styles.regoContainer}>
<p>Rego Policy Code</p>
<pre>
<code>{policy.regoContent}</code>
</pre>
<Code code={policy.regoContent} language={"rego"} />
</div>
</>
) : (
Expand Down
2 changes: 2 additions & 0 deletions pages/policies/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
import React from "react";
import PolicyForm from "components/policies/PolicyForm";
import { EXAMPLE_POLICY } from "utils/constants";

const NewPolicy = () => {
return (
Expand All @@ -24,6 +25,7 @@ const NewPolicy = () => {
endpoint={"/api/policies"}
verb={"create"}
submitButtonText={"Save Policy"}
policy={{ regoContent: EXAMPLE_POLICY }}
/>
);
};
Expand Down
Loading