translate example is not working - admin-on-rest

I'm trying to use the example of translating your own component
so i'm doing this in app.js:
const messages = {
en: {
myroot: {
hello: {
world: 'Hello, World!',
},
},
},
};
const App = () => (
<Admin message={messages} locale="en" ...>
<Resource name="myresource" edit={EditPage} />
and in my Translation component:
import React from 'react';
import { translate } from 'admin-on-rest';
const Translation = ({ translate }) => (
<button>{translate('myroot.hello.world')}</button>
);
export default translate(Translation);
finally in my EditPage:
import Translation from 'path/to/Translation';
export const EditPage = (props) => (
<Edit {...props}>
<Translation />
</Edit>
);
its not working for me. its just showing myroot.hello.world in the button.
could you please help me out with that?

Typo ?
<Admin messages={messages} locale="en" ...>
Note that the prop is messages and not message

Related

Apollo GraphQL failing connection

My root component is already wrapped with an ApolloProvider tag, but the error message tells me it is not.
Error Message
Invariant Violation: Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance in via options.
This error is located at:
in App (created by ExpoRoot)
Problem is my root component is already wrapped with an ApolloProvider tag
React Native Code
IMPORT statements
import {
ApolloClient,
InMemoryCache,
useQuery,
ApolloProvider,
gql,
} from "#apollo/client";
CONNECTION WITH GraphQL
const client = new ApolloClient({
uri: "https://www.outvite.me/gql/gql",
cache: new InMemoryCache(),
defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network' } },
})
TEST QUERY
const USER_QUERY = gql`
query USER {
users {
nodes {
edge {
username
}
}
}
}
`
DEFAULT APP
THIS IS WHERE THE ERROR IS BEING THROWN
const { data, loading } = useQuery(USER_QUERY) is the line that traceback shows
export default function App() {
const { data, loading } = useQuery(USER_QUERY)
return (
<ApolloProvider client={client}>
<View>
<Text style={styles.text}>Open</Text>
<Text style={styles.text}>Another text</Text>
</View>
<Button title="Toggle Sidebar" onPress={() => toggleSidebarView()} />
<Button title="Change theme" onPress={() => toggleColorTheme()} />
</ApolloProvider>
);
}
If I'm not mistaken, the useQuery hook only works if you're in a component that is already wrapped in the ApolloProvider so you probably want to do something like this
export default function MainApp() {
const { data, loading } = useQuery(USER_QUERY)
return (
<View>
... use 'data' in here somewhere...
</View>
);
}
and then the top-level App component would look like
export default function App() {
return (
<ApolloProvider client={client}>
<MainApp />
</ApolloProvider>
);
}

i18next with HttpBackend, Trans looking for key before init finished

I'm loading translations dynamically, with the HttpBackend
However inside my component I use the Trans component, and it is screaming about missing translation key, I also see the init finished after it tried to access the Trans component.
I have a Suspens around my app, why is this happens?
I get in the console:
i18next::translator: missingKey en translation myKey This is text that has a in it.... 
the init happens after.
How can I fix this?
// file: i18n.js
i18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-http-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
fallbackLng: 'en',
debug: true,
supportedLngs: ['en', 'bg', 'rs'],
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
backend: {
loadPath: '/static/{{lng}}.translations.json',
},
react: {
wait: true,
useSuspense: true,
},
transSupportBasicHtmlNodes: true,
});
export default i18n;
// app.js
function App() {
return (
<BrowserRouter>
<Apollo>
<Suspense fallback={<Loading />}>
<ThemeProvider theme={theme}>
<Header />
<Routes />
<Footer />
</ThemeProvider>
</Suspense>
</Apollo>
</BrowserRouter>
);
}
problematic component:
//home
const I18N_TEXT_KEY_CONSTANT = 'text_key';
const Home = () => (
<Trans i18nKey={I18N_TEXT_KEY_CONSTANT}>
This is text that has a <br /> in it and also some random spaces.
</Trans>
);
You should pass the t function to Trans component.
//home
import { useTranslation } from 'react-i18next';
const I18N_TEXT_KEY_CONSTANT = 'text_key';
const Home = () => {
const { t } = useTranslation();
return (
<Trans i18nKey={I18N_TEXT_KEY_CONSTANT} t={t}>
This is text that has a <br /> in it and also some random spaces.
</Trans>
);
};
The solution which is sadly not document is:
const {t} = useTranstions()
<Trans i18nKey="someKey" t={t}>
Passing the t into the Trans works perfectly.

AOR - UnitTest a simple List

I am using AOR v1.4.0 and trying to write a unit test to test the rendering of a simple list with one row. But nothing gets logged on console as HTML
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as Renderer from 'react-test-renderer';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import {render, shallow, mount} from 'enzyme';
import {Datagrid, List, Admin, Resource} from 'admin-on-rest';
import {CategoryList} from '../Categories';
describe('Categories', ()=>{
it('renders category list correctly', ()=>{
const wrapper = mount(
<Admin title="Mock Admin Client" restClient={ jest.fn().mockImplementation(()=>{
return new Promise((res, rej)=>{
res({
total : 1,
data: ()=>{
return {
id: "0300b4cf-4888-4e73-b4e1-25cf4686e05c",
name: "cat2",
displaySequence: 121
}
}
});
});
})}>
<Resource options={{ label: 'Categories' }} name="category" list={CategoryList}/>
</Admin>
);
console.log(wrapper.html());//<-- does not log anything
});
});
The original component
export const CategoryList = (props: any) => (
<List {...props} perPage={50}>
<Datagrid>
<TextField source="id" />
<TextField source="name" />
<TextField source="displaySequence" />
<EditButton/>
<ShowButton/>
</Datagrid>
</List>
);
Can some one please modify & suggest on how to mock the restClient using JEST ? I guess that is the place I am going wrong.
Also, is there a better way to test the list in isolation ?
As your restClient is async, you have to wait for the next tick to get something in return, see https://stackoverflow.com/a/43855794/1333479

How to richly style AOR Edit page

Have to create an edit Page editing a number of parameters on an instance of a'tale' resource.
However adding any element such as an MUI Card or even a div, is causing the app to freeze in various ways.
These are the approaches I have tried.
1) Adding a card component or placing my elements within a div for styling
export const EditorEditTale = (props) => {
return (
<Edit {...props} title="Tale Editor">
<SimpleForm >
<div>
<Image />
<TaleCardHeader props={ props } style={taleCardHeaderStyle.editor} />
</div>
</SimpleForm>
</Edit>
)
};
This is causing nothing to render.
Second approach, assuming that the record and basePath arent getting propagated to the children completely. Trying to use component like below.
const Input = ({record, basePath}) => {
return (
<div>
<LongTextInput source="taleText" />
</div>
)
}
This is causing the page to not render with everything in some kind of locking loop with the error - cannot read property touched of undefined.
How should I create a custom Edit page with a complex inputs and styling.
UPDATE: Been trying to write a custom form to substitute the SimpleForm component with no luck so far.
To create a custom form you can follow these steps:
make an exact copy of SimpleForm to your project.
rename SimpleForm to what you want.
fix all the relative imports.
test the new form until it works.
I made a minimum working form based on current master branch's SimpleForm
import React, { Children, Component } from 'react';
import PropTypes from 'prop-types';
import { reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import getDefaultValues from 'admin-on-rest/mui/form/getDefaultValues';
import FormField from 'admin-on-rest/mui/form/FormField';
import Toolbar from 'admin-on-rest/mui/form/Toolbar';
const formStyle = { padding: '0 1em 1em 1em' };
export class PostForm extends Component {
handleSubmitWithRedirect = (redirect = this.props.redirect) => this.props.handleSubmit(values => this.props.save(values, redirect));
render() {
const { children, invalid, record, resource, basePath, submitOnEnter, toolbar } = this.props;
return (
<form className="simple-form">
<Field name="name_of_a_field" component="input" />
{toolbar && React.cloneElement(toolbar, {
handleSubmitWithRedirect: this.handleSubmitWithRedirect,
invalid,
submitOnEnter,
})}
</form>
);
}
}
PostForm.propTypes = {
basePath: PropTypes.string,
children: PropTypes.node,
defaultValue: PropTypes.oneOfType([
PropTypes.object,
PropTypes.func,
]),
handleSubmit: PropTypes.func, // passed by redux-form
invalid: PropTypes.bool,
record: PropTypes.object,
resource: PropTypes.string,
redirect: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
]),
save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission
submitOnEnter: PropTypes.bool,
toolbar: PropTypes.element,
validate: PropTypes.func,
};
PostForm.defaultProps = {
submitOnEnter: true,
toolbar: <Toolbar />,
};
const enhance = compose(
connect((state, props) => ({
initialValues: getDefaultValues(state, props),
})),
reduxForm({
form: 'record-form',
enableReinitialize: true,
}),
);
export default enhance(PostForm);
The above code works for AOR's example.
I hope this helps.
(import might be slightly different when you have AOR as npm dependency :
import getDefaultValues from 'admin-on-rest/lib/mui/form/getDefaultValues';
import FormField from 'admin-on-rest/lib/mui/form/FormField';
import Toolbar from 'admin-on-rest/lib/mui/form/Toolbar';
)
Documenting my final answer. You have to create a custom Redux Form. You can use AOR Input components straight. They come prewrapped for Redux Form.
import { Field, reduxForm } from 'redux-form';
import compose from 'recompose/compose';
import { connect } from 'react-redux';
class StyledForm extends Component {
// Newer version of aor needs this function defined and passed to save buttons. All props are being passed by parent List component.
handleSubmitWithRedirect = (redirect = this.props.redirect) => this.props.handleSubmit(values => this.props.save(values, redirect));
render() {
const { handleSubmit, invalid, record, resource, basePath } = this.props
return (<div>
<form onSubmit={handleSubmit} >
<Card >
<CardText >
//This component simply displays data, something not possible very easily with SimpleForm.
<HeaderComp basePath={basePath} record={record} />
<Field source="category_id"
optionText="categoryName"
reference="categories"
resource={resource}
record={record}
basePath={basePath}
name="NAME OF THE FIELD IN YOUR REDUX DATASTORE"
component={REFERENCEFIELDCOMP} />
//create complex div structures now.
<div >
<span>Tale</span>
<Field resource={resource} record={record} basePath={basePath} name="taleText" component={TextInput} />
</div>
</CardText >
<MuiToolbar>
<ToolbarGroup>
<SaveButton handleSubmitWithRedirect={this.handleSubmitWithRedirect}/>
//Add custom buttons with custom actions
<Field record={record} name="status" component={EditButtons} />
</ToolbarGroup>
</MuiToolbar>
</Card>
</form>
</div>)
}
};
const enhance = compose(
connect((state, props) => ({
initialValues: getDefaultValues(state, props),
})),
reduxForm({
form: 'record-form',
enableReinitialize: true,
}),
);
export default enhance(StyledForm);
You will have to either import or copy getDefaultValues from AOR in the node modules.
I copied it into the file below.
import getDefaultValues from '../functions/getDefaultValues';
If you need a referenceField in your field. Then wrap it in a custom component like shown below
const DropDownSelector = ({optionText, ...props}) => {
return (
<ReferenceInput {...props} label="" >
<SelectInput optionText={optionText} />
</ReferenceInput>
)
}

Values passed to redux-form initialValues are not rendered

I have a problem rendering initialValues passed to a form using redux-form/immutable (v6.0.5) with a custom component:
// WRAPPER COMPONENT
import React from 'react';
import {connect} from 'react-redux';
import DocumentsEditForm from './documentsEditForm';
import {documentsGetOneInfo} from './../../../modules/documents/actions/documents_get_one_info';
import {documentsPut} from './../../../modules/documents/actions/documents_put';
#connect((state) => {
return ({
selectedDocument: state.getIn(['documents', 'selectedDocument']).toJS()
});
})
class DocumentsEdit extends React.Component {
static propTypes = {
dispatch: React.PropTypes.func,
isLoading: React.PropTypes.bool,
routing: React.PropTypes.object,
selectedDocument: React.PropTypes.object
};
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentWillMount() {
this.props.dispatch(adminDocumentsGetOneInfo(this.props.routing.locationBeforeTransitions.query.id));
}
handleSubmit(data) {
this.props.dispatch(documentsPut(data));
}
render() {
const initialValues = (this.props.selectedDocument.id) ? {
filename: this.props.selectedDocument.filename,
id: this.props.selectedDocument.id
} : {};
return (
<DocumentsEditForm
enableReinitialize
initialValues={initialValues}
onSubmitDocumentsEditForm={this.handleSubmit}
/>
);
}
}
export default DocumentsEdit;
Here is the form component itself:
import React from 'react';
import {Field, reduxForm} from 'redux-form/immutable';
import {Button} from 'react-bootstrap';
import InputTextRedux from './../../shared/components/input_text_redux';
import InputSelectRedux from './../../shared/components/input_select_redux';
let DocumentsEditForm = (props) => {
const {error, handleSubmit, pristine} = props;
return (
<form
onSubmit={handleSubmit(props.onSubmitDocumentsEditForm)}
>
<Field
component={InputTextRedux}
disabled
name="id"
type="text"
/>
<Field
component={InputTextRedux}
name="filename"
type="text"
/>
<Button
type="submit"
>
{'Save'}
</Button>
</form>
);
};
DocumentsEditForm.propTypes = {
error: React.PropTypes.object,
handleSubmit: React.PropTypes.func,
onSubmitDocumentsEditForm: React.PropTypes.func,
pristine: React.PropTypes.bool
};
documentsEditForm = reduxForm({
form: 'documentsEditForm'
})(DocumentsEditForm);
export default DocumentsEditForm;
And here is the custom InputTextRedux component:
import React from 'react';
import {ControlLabel, FormControl, FormGroup, InputGroup} from 'react-bootstrap';
const InputTextRedux = (props) => {
const {
id,
disabled,
input,
type
} = props;
return (
<div>
<FormGroup
name={input.name ? input.name : ''}
>
<ControlLabel>
{'Label'}
</ControlLabel>
<InputGroup>
<FormControl
disabled={disabled ? true : false}
id={id ? id : input.name}
onChange={(event) => {
input.onChange(event.target.value);
}}
type={type ? type : 'text'}
value={input.value ? input.value : ''}
/>
<FormControl.Feedback />
</InputGroup>
</FormGroup>
</div>
);
};
InputTextRedux.propTypes = {
disabled: React.PropTypes.bool,
id: React.PropTypes.string,
input: React.PropTypes.object,
type: React.PropTypes.string,
};
export default InputTextRedux;
I can submit the form and get the correct initialValues passed to handleSubmit but I can't seem to get the values displayed.
According to DevTools redux-form/INITIALIZE gets called with the correct payload and also the state at state->form->documentsEditForm->values/initial is correctly updated.
I also tried loading fixed initialValues to rule out problems with the api call delay but I got the same result. "input: {value: ''}" is always an empty string in the Field props.
What I noticed was that inside the props the form component recieves the structure looks as follows:
props {
....
initialValues: {
__altered: false,
_root: {
entries: [
[
'id',
'123456'
],
[
'filename',
'test.txt'
]
]
},
size: 2
},
....
}
If I try to set a field's value directly to "props.initialValues._root.entries[0][1] for example it works.
Also I noticed since the full form has a Field with name="size" that this value gets filled correctly with the value "2" from initialValues above. So it seems as if the form is looking at the wrong "level" of initialValues for the values. To test this I also tried naming a Field "__altered" and its value was correctly set to false.
What am I doing wrong? Thanks alot!
This has been resolved: See https://github.com/erikras/redux-form/issues/1744#issuecomment-251140419
Basically I had to import "redux-form/immutable" instead of "redux-form".

Resources