Skip to content

Commit aa62eab

Browse files
Special characters and Placeholders (#223)
* Add decoder utility and tests for it * Use decodeText in the component * Move placeholders and seeds to separate constants. Use them in the editor * Add placeholder color * Update default text. Co-authored-by: Mackenzie <[email protected]>
1 parent 3bb448e commit aa62eab

File tree

6 files changed

+59
-52
lines changed

6 files changed

+59
-52
lines changed

src/components/Arguments/SingleArgument/styles.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export const Input = styled.input`
4444
margin-bottom: 0;
4545
}
4646
box-sizing: border-box;
47+
48+
::placeholder {
49+
color: #999;
50+
}
4751
`;
4852

4953
export const Error = styled.p`

src/components/MdeEditor.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import SimpleMDE from "react-simplemde-editor";
3+
import { PLACEHOLDER_README } from "providers/Project/projectDefault";
34
import "easymde/dist/easymde.min.css";
45
import "unreset-css/dist/unreset.min.css"; // restore browser default element styles
56

@@ -14,6 +15,9 @@ export const MdeEditor: React.FC<{
1415
className="unreset"
1516
value={value}
1617
onChange={v => onChange(v)}
18+
options={{
19+
placeholder: PLACEHOLDER_README
20+
}}
1721
/>
1822
)
1923
}

src/containers/Editor/components.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import { Editor as EditorRoot } from 'layout/Editor';
1010
import { Heading } from 'layout/Heading';
1111
import { EntityType, ActiveEditor } from 'providers/Project';
1212
import { useProject } from 'providers/Project/projectHooks';
13+
import { PLACEHOLDER_DESCRIPTION, PLACEHOLDER_TITLE } from "providers/Project/projectDefault";
1314
import {Account, Project} from 'api/apollo/generated/graphql';
1415

16+
1517
import debounce from 'util/debounce';
1618
import Mixpanel from 'util/mixpanel';
1719

@@ -29,6 +31,8 @@ import {
2931
Label,
3032
} from 'components/Arguments/SingleArgument/styles';
3133
import { Markdown } from 'components/Markdown';
34+
35+
import { decodeText } from "util/readme";
3236
import { CadenceLanguageServer, Callbacks } from "util/language-server";
3337
import { MonacoServices } from "monaco-languageclient/lib/monaco-services";
3438
import * as monaco from "monaco-editor";
@@ -214,9 +218,11 @@ const EditorContainer: React.FC<EditorContainerProps> = ({
214218
project,
215219
active,
216220
}) => {
217-
const [title, setTitle] = useState<string | undefined>(project.title);
221+
const [title, setTitle] = useState<string | undefined>(
222+
decodeText(project.title)
223+
);
218224
const [description, setDescription] = useState<string | undefined>(
219-
project.description,
225+
decodeText(project.description)
220226
);
221227
const [readme, setReadme] = useState<string | undefined>(project.readme);
222228

@@ -371,6 +377,7 @@ const EditorContainer: React.FC<EditorContainerProps> = ({
371377
<Label>Title</Label>
372378
<Input
373379
value={title}
380+
placeholder={PLACEHOLDER_TITLE}
374381
onChange={(event) => {
375382
setTitle(event.target.value);
376383
updateProject(event.target.value, description, readme);
@@ -381,6 +388,7 @@ const EditorContainer: React.FC<EditorContainerProps> = ({
381388
<Label>Description</Label>
382389
<Input
383390
value={description}
391+
placeholder={PLACEHOLDER_DESCRIPTION}
384392
onChange={(event) => {
385393
setDescription(event.target.value);
386394
updateProject(title, event.target.value, readme);

src/providers/Project/projectDefault.ts

Lines changed: 12 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -135,60 +135,22 @@ const DEFAULT_SCRIPT = `pub fun main(): Int {
135135
}
136136
`;
137137

138+
export const SEED_TITLE = "Cadence Playground"
139+
export const SEED_DESCRIPTION = "Showcase Cadence interactions"
140+
export const SEED_README = ""
141+
142+
export const PLACEHOLDER_TITLE = "Name of your project"
143+
export const PLACEHOLDER_DESCRIPTION = "Single sentence describing this project"
144+
export const PLACEHOLDER_README = `Here you can provide a detailed explanation to help others understand how to use your Playground project.
145+
Editor supports Markdown. Please, consult https://www.markdownguide.org/cheat-sheet/ for examples and tips.`
146+
138147
export function createDefaultProject(): Project {
139148
return createLocalProject(
140149
null,
141150
strToSeed(uuid()),
142-
"Default project title ~ Hello World!",
143-
"Default project description ~ This project demonstrates...",
144-
`# My Project README!
145-
146-
[![](https://avatars.githubusercontent.com/u/1680273?s=80&v=4)](https://avatars.githubusercontent.com/u/1680273?v=4)
147-
148-
**This** is an *example* ~~text~~.
149-
150-
Unordered list example:
151-
152-
* hi
153-
* hello
154-
* howdy
155-
156-
> This is a famous quote
157-
158-
[Now try a link](https://www.onflow.org/)
159-
160-
161-
And some code:
162-
163-
\`\`\`
164-
// HelloWorld.cdc
165-
//
166-
// Welcome to Cadence! This is one of the simplest programs you can deploy on Flow.
167-
//
168-
// The HelloWorld contract contains a single string field and a public getter function.
169-
//
170-
// Follow the "Hello, World!" tutorial to learn more:
171-
// https://docs.onflow.org/cadence/tutorial/02-hello-world/
172-
173-
access(all) contract HelloWorld {
174-
175-
// Declare a public field of type String.
176-
//
177-
// All fields must be initialized in the init() function.
178-
access(all) let greeting: String
179-
180-
// The init() function is required if the contract contains any fields.
181-
init() {
182-
self.greeting = "Hello, World!"
183-
}
184-
185-
// Public function that returns our friendly greeting!
186-
access(all) fun hello(): String {
187-
return self.greeting
188-
}
189-
}
190-
\`\`\`
191-
`,
151+
SEED_TITLE,
152+
SEED_DESCRIPTION,
153+
SEED_README,
192154
DEFAULT_ACCOUNTS,
193155
[{ title: "Transaction", code: DEFAULT_TRANSACTION }],
194156
[{ title: "Script" , code :DEFAULT_SCRIPT }]

src/util/readme.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Title and Description fields come from server in sanitized form, escaping all special characters
2+
// for security reasons. For user friendly display we need to convert it back to readable form
3+
//
4+
// Original solution was found on StackOverflow: https://stackoverflow.com/a/42254787/3892712
5+
export const decodeText = (text: string): string => {
6+
const parser = new DOMParser();
7+
const dom = parser.parseFromString(
8+
`<!doctype html><body>${text}</body></html>`,
9+
'text/html',
10+
);
11+
return dom.body.textContent;
12+
};

test/util/readme.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { decodeText } from '../../src/util/readme';
2+
3+
describe('Decoding', () => {
4+
test('properly decodes input - multiple special characters', () => {
5+
const input = `&#39;&amp;&#34;/\\\\~`;
6+
const expected = `'&"/\\\\~`;
7+
const result = decodeText(input);
8+
expect(result).toBe(expected);
9+
});
10+
11+
test('properly decodes input - human readable text', () => {
12+
const input = `We&#39;re just a @silver-boy &amp; /bilbo-baggins\\`;
13+
const expected = `We're just a @silver-boy & /bilbo-baggins\\`;
14+
const result = decodeText(input);
15+
expect(result).toBe(expected);
16+
});
17+
});

0 commit comments

Comments
 (0)