diff --git a/src/components/App/__snapshots__/test.js.snap b/src/components/App/__snapshots__/test.js.snap
index dd7daf4..8cc359f 100644
--- a/src/components/App/__snapshots__/test.js.snap
+++ b/src/components/App/__snapshots__/test.js.snap
@@ -1,11 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Message rendering 1`] = `
-
-
- Placeholder app content
-
-
+
+
+
+
+ Placeholder app content
+
+
+
`;
diff --git a/src/components/App/index.js b/src/components/App/index.js
index 743c5c1..b520908 100644
--- a/src/components/App/index.js
+++ b/src/components/App/index.js
@@ -1,11 +1,29 @@
import React from 'react';
+import Form from 'components/Form';
import Message from 'components/Message';
-const App = () => (
+const syntaxes = {
+ js: 'JavaScript',
+ pcre: 'PCRE'
+};
+
+const downloadUrls = [
+ { url: '#svg', filename: 'image.svg', type: 'image/svg+xml', label: 'Download SVG' },
+ { url: '#png', filename: 'image.png', type: 'image/png', label: 'Download PNG' }
+];
+
+const handleSubmit = ({ expr, syntax}) => console.log(syntax, expr); // eslint-disable-line no-console
+
+const App = () =>
+
Placeholder app content
-);
+;
export default App;
diff --git a/src/components/Form/__snapshots__/test.js.snap b/src/components/Form/__snapshots__/test.js.snap
new file mode 100644
index 0000000..65b68d0
--- /dev/null
+++ b/src/components/Form/__snapshots__/test.js.snap
@@ -0,0 +1,46 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Form rendering 1`] = `
+
+`;
diff --git a/src/components/Form/index.js b/src/components/Form/index.js
new file mode 100644
index 0000000..ff8b6c4
--- /dev/null
+++ b/src/components/Form/index.js
@@ -0,0 +1,94 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { translate, Trans } from 'react-i18next';
+
+import DownloadIcon from 'feather-icons/dist/icons/download.svg';
+import LinkIcon from 'feather-icons/dist/icons/link.svg';
+import ExpandIcon from 'feather-icons/dist/icons/chevrons-down.svg';
+
+import style from './style.css';
+
+class Form extends React.Component {
+ handleSubmit = event => {
+ event.preventDefault();
+ this.props.onSubmit.call(this, {
+ expr: this.textarea.value,
+ syntax: this.syntax.value
+ });
+ }
+
+ handleKeyPress = event => {
+ if (event.charCode === 13 && event.shiftKey) {
+ this.handleSubmit(event);
+ }
+ }
+
+ textareaRef = textarea => this.textarea = textarea
+
+ syntaxRef = syntax => this.syntax = syntax
+
+ permalinkAction() {
+ const { permalinkUrl } = this.props;
+
+ if (!permalinkUrl) {
+ return;
+ }
+
+ return
+ Permalink
+ ;
+ }
+
+ downloadActions() {
+ const { downloadUrls } = this.props;
+
+ if (!downloadUrls) {
+ return;
+ }
+
+ return downloadUrls.map(({ url, filename, type, label }, i) =>
+
+ { label }
+
+ );
+ }
+
+ render() {
+ const { syntaxes, t } = this.props;
+
+ return
+
+
+
;
+ }
+}
+
+Form.propTypes = {
+ syntaxes: PropTypes.object,
+ onSubmit: PropTypes.func,
+ permalinkUrl: PropTypes.string,
+ downloadUrls: PropTypes.array,
+ t: PropTypes.func
+};
+
+export default translate()(Form);
+export { Form };
diff --git a/src/components/Form/style.css b/src/components/Form/style.css
new file mode 100644
index 0000000..e99f3ef
--- /dev/null
+++ b/src/components/Form/style.css
@@ -0,0 +1,75 @@
+@import url('../../globals.css');
+
+:root {
+ --control-gradient: var(--color-green) var(--gradient-green);
+ --select-height: 2.8rem;
+ --select-width: 12rem;
+}
+
+.form {
+ margin: var(--spacing-margin) 0;
+
+ & textarea {
+ display: block;
+ font-size: inherit;
+ line-height: 1.5em;
+ border: 0 none;
+ outline: none;
+ background: var(--color-tan);
+ padding: 0 1rem;
+ margin-bottom: var(--spacing-margin);
+ width: 100% !important; /* "!important" to prevent user changing width */
+ box-sizing: border-box;
+ font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
+ }
+
+ & textarea::placeholder {
+ color: var(--color-brown);
+ }
+
+ & button {
+ font-size: inherit;
+ font-weight: bold;
+ line-height: 2.8rem;
+ width: 10rem;
+ border: 0 none;
+ background: var(--control-gradient);
+ cursor: pointer;
+ padding: 0;
+ margin-right: 1rem;
+ }
+}
+
+.actions {
+ float: right;
+
+ & svg {
+ height: 1em;
+ vertical-align: middle;
+ }
+}
+
+.select {
+ position: relative;
+ vertical-align: bottom;
+ display: inline-block;
+ height: var(--select-height);
+ width: var(--select-width);
+ background: var(--control-gradient);
+ overflow: hidden;
+
+ & select {
+ background: transparent;
+ border: 0 none;
+ font-size: inherit;
+ height: var(--select-height);
+ width: calc(var(--select-width) + 2rem);
+ }
+
+ & svg {
+ position: absolute;
+ top: calc((var(--select-height) - 24px) / 2);
+ right: 0;
+ pointer-events: none;
+ }
+}
diff --git a/src/components/Form/test.js b/src/components/Form/test.js
new file mode 100644
index 0000000..77a4f25
--- /dev/null
+++ b/src/components/Form/test.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { Form } from 'components/Form';
+import translate from '__mocks__/translate';
+
+const syntaxes = {
+ js: 'Javascript',
+ pcre: 'PCRE'
+};
+
+describe('Form', () => {
+ test('rendering', () => {
+ const component = shallow(
+
+ );
+ expect(component).toMatchSnapshot();
+ });
+
+ test('rendering with download URLs');
+
+ test('rendering with permalink URL');
+
+ test('submitting form');
+
+ test('submitting form with Shift+Enter');
+});
diff --git a/src/locales/en/translation.yaml b/src/locales/en/translation.yaml
index 8c190a4..a5e2a54 100644
--- a/src/locales/en/translation.yaml
+++ b/src/locales/en/translation.yaml
@@ -1,7 +1,10 @@
404 Page Not Found: "404: Page Not Found"
An error has occurred: An error has occurred
Created by <1>Jeff Avallone1>: Created by <1>Jeff Avallone1>
+Display: Display
+Enter regular expression to display: Enter regular expression to display
Generated images licensed: "Generated images licensed: <1><0>0>1>"
+Permalink: Permalink
Source on GitHub: Source on GitHub
The page you have requested could not be found: The page you have requested could not be found
This error has been logged: This error has been logged. You may also <1>fill out a report1>.