Split string from Redux-Form Field into multiple data points - react-redux

I'm currently working on a project that uses redux-form fields. We make use of the react-google-autocomplete component to allow users to enter an address in a similar fashion to if they were typing it in Google Maps.
Currently, we strip out the name of the location (if there is one) and just store the address. (So if I typed in "The White House", and selected the suggestion of "The White House, Pennsylvania Avenue Northwest, Washington, DC", we actually end up storing "1600 Pennsylvania Avenue Northwest, Washington, DC".
import React, { PropTypes } from 'react';
import { Field } from 'redux-form';
import Autocomplete from 'react-google-autocomplete';
function emulateTabPress(currentEl) {
const formEls = Array.from(currentEl.form.elements);
const currentIdx = formEls.findIndex(el => el === currentEl);
formEls[currentIdx + 1].focus();
}
function getFormattedGoogleAddress(googleParams) {
return googleParams.formatted_address || googleParams.name;
}
function renderGoogleAutoComplete(props) {
return (
<Autocomplete
type="text"
name="location"
onPlaceSelected={
param => props.input.onChange(getFormattedGoogleAddress(param))
}
types={[]}
value={props.input.value}
onChange={newValue => props.input.onChange(newValue)}
onKeyPress={event => {
if (event.key === 'Enter') {
event.preventDefault();
emulateTabPress(event.target);
}
}}
/>
);
}
renderGoogleAutoComplete.propTypes = {
input: PropTypes.shape({
value: PropTypes.string,
onChange: PropTypes.func
})
};
function AutocompleteLocation({ name, required }) {
return (
<Field
name={name}
required={required}
component={renderGoogleAutoComplete}
/>
);
}
AutocompleteLocation.propTypes = {
name: PropTypes.string.isRequired,
required: PropTypes.bool
};
export default AutocompleteLocation;
What I WANT to do, is store three separate pieces of information
The address (googleParam.formatted_address)
The name of the location (googleParam.name)
The Google ID for the location (googleParam.id)
I've written code that stores this as an object and used that as the value in my component, but then when I try to use any of the values from the store later on, it just shows as "object object"
Any suggestions on how to get these values into discrete data elements?

Related

How to wrap text in Polaris Index Table cell?

am using Polaris Index Table to display some data in my Shopify app. One of the cells in my table has a long string of text and I want to make it wrap so that it fits the size of the screen. Is there a way to do this in Polaris?
Here is my code:
import {IndexTable, Card, useIndexResourceState, Text} from '#shopify/polaris';
import React from 'react';
function SimpleIndexTableExample() {
const customers = [
{
id: '3411',
url: 'customers/341',
name: 'Mae Jemison long text here, very very long......',
location: 'Decatur, USA',
orders: 20,
amountSpent: '$2,400',
},
{
id: '2561',
url: 'customers/256',
name: 'Ellen Ochoa',
location: 'Los Angeles, USA',
orders: 30,
amountSpent: '$140',
},
];
const resourceName = {
singular: 'customer',
plural: 'customers',
};
const {selectedResources, allResourcesSelected, handleSelectionChange} =
useIndexResourceState(customers);
const rowMarkup = customers.map(
({id, name, location, orders, amountSpent}, index) => (
<IndexTable.Row
id={id}
key={id}
selected={selectedResources.includes(id)}
position={index}
>
<IndexTable.Cell>
<Text variant="bodyMd" fontWeight="bold" as="span">
{name}
</Text>
</IndexTable.Cell>
<IndexTable.Cell>{location}</IndexTable.Cell>
<IndexTable.Cell>{orders}</IndexTable.Cell>
<IndexTable.Cell>{amountSpent}</IndexTable.Cell>
</IndexTable.Row>
),
);
return (
<Card>
<IndexTable
resourceName={resourceName}
itemCount={customers.length}
selectedItemsCount={
allResourcesSelected ? 'All' : selectedResources.length
}
onSelectionChange={handleSelectionChange}
headings={[
{title: 'Name'},
{title: 'Location'},
{title: 'Order count'},
{title: 'Amount spent'},
]}
>
{rowMarkup}
</IndexTable>
</Card>
);
}
I've tried modifying the cells, styles, etc.
To make the text wrap in a cell in a Polaris IndexTable, you can apply the textWrap property to the Text component that you are using within the cell.
You may try this,
import {IndexTable, Card, useIndexResourceState, Text} from '#shopify/polaris';
import React from 'react';
function SimpleIndexTableExample() {
// ...
const rowMarkup = customers.map(
({id, name, location, orders, amountSpent}, index) => (
<IndexTable.Row
id={id}
key={id}
selected={selectedResources.includes(id)}
position={index}
>
<IndexTable.Cell>
<Text variant="bodyMd" fontWeight="bold" as="span" textWrap>
{name}
</Text>
</IndexTable.Cell>
<IndexTable.Cell>{location}</IndexTable.Cell>
<IndexTable.Cell>{orders}</IndexTable.Cell>
<IndexTable.Cell>{amountSpent}</IndexTable.Cell>
</IndexTable.Row>
),
);
// ...
}
I found a workaround for this – in my case there was a HTML representation of a text string available. Then I have used following construction in IndexTable.Row content:
<div dangerouslySetInnerHTML={{__html: myHTMLContent}} />
This allowed using line breaks and other HTML that can be formatted in a way you need. Of course I understand that this adds another problem of creating HTML string, but this is just a workaround.
P.S. that this method adds a risk of XSS attack so can recommend to use this only if you 100% understand the security side.

Get state from select form in child component with Gatsby

I code a Gatsby app with a Main page and two components. The value from a select form will be used to query a Postgresql database through a graphql query.
What I can already do: in the form component, I get the value from the select menu and pass it from this child component to the parent (the main page). In the data component, I can query the database with graphql and get the results with hardcoded values.
What I can't do yet: get the value from the select component to the data component and use it in my graphql query.
I tried different ways to get the value without success using this.props.value1 or this.state.value1. I also tested a simple component to make sure I could get the value from the parent to a child component and it worked seamlessly. So it's the way I try to import the value in a querying component that is the problem.
**//Data component**
let val = 88 //for hardcoded test. That works.
const DataPage = () => {
const data = useStaticQuery(query)
return (
<div>
<p>From Postgres: {data.postgres.allEstivalsList[val].nbr}</p>
</div>
)
}
const query = graphql`
{
postgres {
allAveragesList {
avg
}
allEstivalsList {
year
nbr
}
}
}
`;
export default DataPage;
**//Main page**
export default class App extends React.Component {
constructor(props) {
super(props);
}
state = {
value1: null,
// other values
}
render() {
return (
<div>
<p>Get state in main page: {this.state.value1}</p>
<DataPage val = {this.state.value1} />
<SelectForm clickHandler={y => { this.setState({ value1: y }); }} />
</div>
)
}
}
**//Form component**
export default class IndexPage extends React.Component {
state = {
value1: null,
}
handleClick = () => {
this.props.clickHandler(this.state.value1);
this.setState(prevState => {
return { value1: prevState.value1 };
});
};
render() {
return (
<Formlayout>
<p>Test -- Value for the selected year: {this.state.value1}</p>
<select onChange = {(e) => this.setState({ value1: e.target.value })}>
<option value="">-- Year --</option>
<option value="1">1901</option>
<option value="2">1902</option>
<option value="3">1903</option>
</select>
<button onClick={this.handleClick}>Go!</button>
</Formlayout>
)
}
}
I'd appreciate to get some directions to get the select value in the data component. As my test variable val is effectively working when used in the query, what I'd like to achieve is to apply to that variable the state from the component. And that's where I'm stuck right now.
The GraphQL query is executed at the build time, not at runtime. Have a look at the docs, it's possible to query data at build and at runtime.
https://www.gatsbyjs.org/docs/client-data-fetching/
// edit
Normally you would pre generate all (static) content pages - e.g. if you have data for 50 years, you let gatsby build those 50 pages and then you could navigate by path to the page.
If you want to pass a variable to the query, check the docs: https://www.gatsbyjs.org/docs/graphql-reference/#query-variables
query GetBlogPosts(
$limit: Int, $filter: MarkdownRemarkFilterInput, $sort: MarkdownRemarkSortInput
) {
allMarkdownRemark(
limit: $limit,
filter: $filter,
sort: $sort
) {
edges {
node {
frontmatter {
title
date(formatString: "dddd DD MMMM YYYY")
}
}
}
}
}

How to get 'Last Update Date' of a blog post in GATSBY.js

Hello I'm not a dev so may the question will be easy for you guys. I used the advance starter from gatsby site. The blog is working perfect but I need to provide the LAST UPDATED time under my title. Searched for some solutions but none of them worked. Could you Provide some help?
gatsby-node.js
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions;
if (node.internal.type !== 'MarkdownRemark') {
return;
}
const fileNode = getNode(node.parent);
createNodeField({
node,
name: 'modifiedTime',
value: fileNode.mtime
});
};
`````````````````````````
PostListing.jsx
class PostListing extends React.Component {
getPostList() {
const postList = [];
this.props.postEdges.forEach(postEdge => {
postList.push({
path: postEdge.node.fields.slug,
tags: postEdge.node.frontmatter.tags,
cover: postEdge.node.frontmatter.cover,
title: postEdge.node.frontmatter.title,
date: postEdge.node.fields.date,
excerpt: postEdge.node.excerpt,
timeToRead: postEdge.node.timeToRead,
modifiedTime:postEdge.node.modifiedTime
});
});
return postList;
}
render() {
const postList = this.getPostList();
return (
<div className='posts'>
{/* Your post list here. */
postList.map(post => (
<Fragment>
<div className='singlePost__date'>
<h4 style={{color:'white'}}> {post.modifiedTime}</h4>
</div>
<div className='singlePost__Title'>
<Link classname='singlePost' to={post.path} key={post.title}>
<h1 className='singlePost__title'>{post.title}</h1>
</Link>
</div>
</Fragment>
))}
</div>
);
}
}
export default PostListing;
I expect something like
TITLE
last updated : 3/2/2019
You can use information stored in Git to get the latest time when a file was modified.
1st approach
Track it manually, but this can be error-prone if you forget to edit the modified time. So I would recommend that as the last option if you can't get others to work.
2nd approach
You can edit your gatsby-node.js to pull information from Git like so:
const { execSync } = require("child_process")
exports.onCreateNode = ({ node, actions }) => {
// ...
if (node.internal.type === "MarkdownRemark") {
const gitAuthorTime = execSync(
`git log -1 --pretty=format:%aI ${node.fileAbsolutePath}`
).toString()
actions.createNodeField({
node,
name: "gitAuthorTime",
value: gitAuthorTime,
})
}
// ...
}
Then, in your template, you can fetch it:
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
# ...
fields {
gitAuthorTime
}
# ...
}
}
And, finally, use it in JSX like so:
import React from "react"
const BlogPost = (props) => {
const { gitAuthorTime } = props.data.markdownRemark.fields
render(<p>Updated at: ${gitAuthorTime}</p>)
}
export default BlogPost
3rd approach
Similar to the previous one but it uses a plugin gatsby-transformer-info. It does a similar thing as in the 2nd approach, but you need to access the modified time differently this time. Like so:
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
# ...
parent {
... on File {
fields {
gitLogLatestDate
}
}
}
# ...
}
}
I wrote more about this in my blog post "Add Updated At To Your Gatsby Blog" if you want to check it out.
Edit: The answer below is actually wrong, since File. modifiedTime is the modifiedTime of the markdown file itself & not the modifiedTime for your content. For example, if you deploy your blog on say, Netlify, then the modifiedTime of your files there will be different than in your local environment.
I think the right answer is to track it separately. If you're using a CMS like NetlifyCMS, you can create a field that automatically update the date/time on every edit.
Wherever you're querying for your markdown files, you can use the below field:
query {
allMarkdownRemark {
edges {
node {
frontmatter { /* other stuff */ }
parent {
... on File {
modifiedTime(formatString: "MM/DD/YYYY")
}
}
}
}
}
}
And access it in your via postEdge.node.parent.modifiedTime

Make react-select 2.0.0 <Async> work with redux-form <Field>

react-select just upgraded to 2.0.0 so google results on the first three pages are all about older versions, even the official document, and none of them helped.
My select box can show all options correctly, but redux form won't pick up the value, with the warning: Warning: A component is changing a controlled input of type hidden to be uncontrolled.
I wonder what have I missed here...
Form component:
<Field
name="residentialAddress"
label = "Residential Address"
type="select"
component={AddressField}
validate={required}
/>
Component
export class AddressField extends Component {
searchAddress = input => {
let options = []
return myPromise(input)
.then(suggestions => {
options = suggestions.map(suggestion =>
({
label: suggestion.label,
data: suggestion.value
})
)
return options;
}
).catch(
error => {
return options = [{ label: "Auto fetching failed, please enter your address manually", value: "", isDisabled: true }];
}
);
};
render() {
const {
input,
label,
meta: { touched, error },
type
} = this.props;
return(
<FormGroup>
<ControlLabel>{label}</ControlLabel>
<Async
{...input}
placeholder={label}
isClearable={true}
getOptionValue={(option) => option.residentialAddress}
onChange = { value => input.onChange(value.data) }
loadOptions={this.searchAddress}
/>
{ touched && error && <span>{error}</span> }
</FormGroup>
)
}
Solution: Simply remove the {...input} in <Async>.
Unlike regular custom Field component where we need to pass in {input}, the react-select Async component seems to take care of itself very well and doesn't require any intervene. Someone may explain it in a more professional way perhaps...
Also worth mention for those who come across this question:
loadOptions with promise used to require object {options: options} as return type. Now it changes to just array (options as in my code). But I didn't find any document that mentions this one.
Hope this could help.

Autoform with Meteor React and Simple-Schema

Is there any possibility to make meteor-autoform work with meteor-collection2-core and react-meteor?
MWE
Preferably I would like to have something like this.
./imports/api/Books.js
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
const Books = new Mongo.Collection("books");
Books.attachSchema(new SimpleSchema({
title: {
type: String,
label: "Title",
max: 200
},
author: {
type: String,
label: "Author"
},
}));
if (Meteor.isServer) {
Meteor.publish('allBooks', function () {
return Books.find({}, );
});
};
export default Books;
./imports/client/NewBooks.js
import React, { Component, PropTypes } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import { quickForm } from 'meteor-autoform';
import Books from '../api/Books';
class NewBooks extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div className="container">
<quickForm
collection={Books}
id="insertBookForm"
type="insert">
</quickForm>
</div>
)
}
};
export default createContainer(() => {
Meteor.subscribe('allBooks');
return {
books: Books.find().fetch()
}
}, NewBooks);
The npm package Uniforms worked super easy with Bootstrap.
Addition to ./imports/client/NewBooks.js
import AutoForm from 'uniforms-unstyled/AutoForm';
...
<AutoForm
schema={Books._collection.simpleSchema()}
onSubmit={doc => console.log(doc)}
/>
To my knowledge, Autoform depends heavily on Blaze, so, you could either use blaze autoform components in react (see here), or you can use a different library for this. I used this in a recent project: github.com/nicolaslopezj/simple-react-form. It's powerful, but much more 'hands-on' than the magical Autoform (you have to write your own form and field components).

Resources