Adding Form component
This commit is contained in:
parent
597cce4566
commit
b83f5cd34d
@ -1,6 +1,33 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Message rendering 1`] = `
|
||||
<React.Fragment>
|
||||
<Translate(Form)
|
||||
downloadUrls={
|
||||
Array [
|
||||
Object {
|
||||
"filename": "image.svg",
|
||||
"label": "Download SVG",
|
||||
"type": "image/svg+xml",
|
||||
"url": "#svg",
|
||||
},
|
||||
Object {
|
||||
"filename": "image.png",
|
||||
"label": "Download PNG",
|
||||
"type": "image/png",
|
||||
"url": "#png",
|
||||
},
|
||||
]
|
||||
}
|
||||
onSubmit={[Function]}
|
||||
permalinkUrl="#permalink"
|
||||
syntaxes={
|
||||
Object {
|
||||
"js": "JavaScript",
|
||||
"pcre": "PCRE",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Message
|
||||
heading="React App"
|
||||
>
|
||||
@ -8,4 +35,5 @@ exports[`Message rendering 1`] = `
|
||||
Placeholder app content
|
||||
</p>
|
||||
</Message>
|
||||
</React.Fragment>
|
||||
`;
|
||||
|
@ -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 = () => <React.Fragment>
|
||||
<Form
|
||||
syntaxes={ syntaxes }
|
||||
downloadUrls={ downloadUrls }
|
||||
permalinkUrl="#permalink"
|
||||
onSubmit={ handleSubmit }/>
|
||||
<Message heading="React App">
|
||||
<p>Placeholder app content</p>
|
||||
</Message>
|
||||
);
|
||||
</React.Fragment>;
|
||||
|
||||
export default App;
|
||||
|
46
src/components/Form/__snapshots__/test.js.snap
Normal file
46
src/components/Form/__snapshots__/test.js.snap
Normal file
@ -0,0 +1,46 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Form rendering 1`] = `
|
||||
<div
|
||||
className="form"
|
||||
>
|
||||
<form
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<textarea
|
||||
autoFocus={true}
|
||||
onKeyPress={[Function]}
|
||||
placeholder="translate(Enter regular expression to display)"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
>
|
||||
<Trans>
|
||||
Display
|
||||
</Trans>
|
||||
</button>
|
||||
<div
|
||||
className="select"
|
||||
>
|
||||
<select>
|
||||
<option
|
||||
key="js"
|
||||
value="js"
|
||||
>
|
||||
Javascript
|
||||
</option>
|
||||
<option
|
||||
key="pcre"
|
||||
value="pcre"
|
||||
>
|
||||
PCRE
|
||||
</option>
|
||||
</select>
|
||||
<SvgMock />
|
||||
</div>
|
||||
<ul
|
||||
className="inline with-separator actions"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
94
src/components/Form/index.js
Normal file
94
src/components/Form/index.js
Normal file
@ -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 <li>
|
||||
<a href={ permalinkUrl }><LinkIcon/><Trans>Permalink</Trans></a>
|
||||
</li>;
|
||||
}
|
||||
|
||||
downloadActions() {
|
||||
const { downloadUrls } = this.props;
|
||||
|
||||
if (!downloadUrls) {
|
||||
return;
|
||||
}
|
||||
|
||||
return downloadUrls.map(({ url, filename, type, label }, i) => <li key={ i }>
|
||||
<a href={ url } download={ filename } type={ type }>
|
||||
<DownloadIcon/>{ label }
|
||||
</a>
|
||||
</li>);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { syntaxes, t } = this.props;
|
||||
|
||||
return <div className={ style.form }>
|
||||
<form onSubmit={ this.handleSubmit }>
|
||||
<textarea
|
||||
ref={ this.textareaRef }
|
||||
onKeyPress={ this.handleKeyPress }
|
||||
autoFocus
|
||||
placeholder={ t('Enter regular expression to display') }></textarea>
|
||||
<button type="submit"><Trans>Display</Trans></button>
|
||||
<div className={ style.select }>
|
||||
<select ref={ this.syntaxRef }>
|
||||
{ Object.keys(syntaxes).map(id => (
|
||||
<option value={ id } key={ id }>{ syntaxes[id] }</option>
|
||||
)) }
|
||||
</select>
|
||||
<ExpandIcon/>
|
||||
</div>
|
||||
<ul className={ ['inline', 'with-separator', style.actions].join(' ') }>
|
||||
{ this.downloadActions() }
|
||||
{ this.permalinkAction() }
|
||||
</ul>
|
||||
</form>
|
||||
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
Form.propTypes = {
|
||||
syntaxes: PropTypes.object,
|
||||
onSubmit: PropTypes.func,
|
||||
permalinkUrl: PropTypes.string,
|
||||
downloadUrls: PropTypes.array,
|
||||
t: PropTypes.func
|
||||
};
|
||||
|
||||
export default translate()(Form);
|
||||
export { Form };
|
75
src/components/Form/style.css
Normal file
75
src/components/Form/style.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
27
src/components/Form/test.js
Normal file
27
src/components/Form/test.js
Normal file
@ -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(
|
||||
<Form t={ translate } syntaxes={ syntaxes }/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('rendering with download URLs');
|
||||
|
||||
test('rendering with permalink URL');
|
||||
|
||||
test('submitting form');
|
||||
|
||||
test('submitting form with Shift+Enter');
|
||||
});
|
@ -1,7 +1,10 @@
|
||||
404 Page Not Found: "404: Page Not Found"
|
||||
An error has occurred: An error has occurred
|
||||
Created by <1>Jeff Avallone</1>: Created by <1>Jeff Avallone</1>
|
||||
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 report</1>.
|
||||
|
Loading…
Reference in New Issue
Block a user