I want a scalable solution, using React, to list some pages and automatically using the last one as my main page.
I have my 'legals mention' page, which will be regularly updated with new content.
import { useTranslation } from "react-i18next";
function Legals() {
const { t } = useTranslation('common');
return (
<div>
<h1>Legals</h1>
<span>Version 2.1 - 4th revision</span>
<span>ยท Effective September 30, 2020</span>
<button>Archived versions</button>
{...}<Content>{...}
</div>
)}
export default Legals;
So this is supposed to display the most recent version, and contains a button with a list to older versions.
I was thinking of using i18n and check in a folder for the last version, is it the best way to do it?
How to accomplish it, at least in pseudocode?
Related
Trying to add an image from my assets folder, but can't seem to reach back that far?
<img class='fade-in' src={'.../assets/images/logos/SWSLogoNOBG.png'} />
Can I not reach back that far with the dots? I can use two and it works, but is three too much?
I've also tried
import SWSLogo from '.../assets/images/logos/SWSLogoNOBG.png';
then done
<img class='fade-in' src={SWSLogo}/>
but that also doesn't work?
I feel super dumb and could use some help cause this one little problem has taken me two days of trying and I've made zero progress.
Edit:
I've made it work, but only by moving my image file into the same folder as my Home.js file.
Can react not reach files outside of the immediate directory? Why is this so stupid?
If you need to go multiple nested folders up then for every folder you need another '../'
for example, if you have
1. src
2. folder
3. folder
index.js
app.js
you would need to do '../../app.js'
if you are using a hard coded path, use quotes:
<img
alt="dolphin"
src="../../images/dolphin.jpg"
ariaLabel="dolphin"
/>
if you are pulling from say a JSON you would do something like:
const animals = {
dolphin: {
image: '../../images/dolphin.jpg',
facts: ['Dolphins have been shown to give distinct names to each other!', 'Dolphins are known to display their own culture!', 'Dolphins have two stomachs!']
},
lobster: {
image: '../../images/lobster.jpg',
facts: ['Lobsters taste with their legs!', 'Lobsters chew with their stomachs!', 'Lobsters can live as long as 100 years.']
},
starfish: {
image: '../../images/starfish.jpg',
facts: ['Starfish can have up to 40 arms!', 'Starfish have no brain and no blood!', 'Starfish can regenerate their own arms!']
}
};
const images = [];
for (const animal in animals) {
images.push (
<img
alt={animal}
src={animals[animal].image}
ariaLabel={animal}
/>
)
};
const allImages =
(
<div className='animals'>
{images}
</div>
)
ReactDOM.render(allImages , document.getElementById('root'));
Previously in Parcel v1 you could just use something like the #svgr/parcel-plugin-svgr plugin for Parcel. This would give you the ability to use SVGs inline like when using CRA:
import Star from './star.svg'
const App = () => (
<div>
<Star />
</div>
)
Can anyone help figure out a way to do this in Parcel 2?
Parcel2 provides the #parcel/transformer-svg-react plugin to accomplish this.
Here's what you need to do:
Install it:
yarn add #parcel/transformer-svg-react --dev
Add a .parcelrc file at the root of your project that defines a named pipleine that uses this plugin:
{
"extends": "#parcel/config-default",
"transformers": {
"jsx:*.svg": ["...", "#parcel/transformer-svg-react"]
"jsx:*": ["..."]
}
}
(The "..." entry should actually be typed into the .parcelrc file - it tells parcel "process these kinds of assets as usual, and only after you are done, transform it into a react component.")
Update (5/2022) I added the "jsx:*": ["..."] line, which appears to currently be a necessary workaround to avoid this bug.
Another current "gotcha" is that if the SVG files that you want to transform into react components contain inline style tags (e.g. <path style="fill:red" ...>, you'll run into this bug. To avoid it, you'll want to remove the #parcel/transformer-svg plugin from your pipeline by modifiying the first transformer line to read "jsx:*.svg": ["#parcel/transformer-svg-react"] (i.e. remove the ...).
Use the named pipeline in your import statements:
import Star from 'jsx:./star.svg'
const App = () => (
<div>
<Star />
</div>
)
(optional, if you use TypeScript). Let TypeScript know about the jsx: named pipeline by using a wildcard module declaration. In a d.ts file that's a part of your typescript project, write this:
declare module "jsx:*.svg" {
import { ComponentType, SVGProps } from "react";
const SVGComponent: ComponentType<SVGProps<SVGSVGElement>>;
export default SVGComponent;
}
See parcel 2 documentation.
I'm using the Kendo Window React wrapper component in my react app, if you look at the docs, the way to open the window is:
$("[data-role='window']").each(function (index) {
$(this).data('kendoWindow').open()
});
But this will open every window on the page. How can I open a single window when multiple windows controls are used?
You're quite right, it will open every window on the page. When using Kendo React wrappers, a recommended approach for dealing with same type widgets is to place them in different containers to help you locate them:
When the Kendo UI for React wrappers are used we recommend placing same type widgets in different containers in order to easily select them as the ID attribute is not directly passed to the new component
I've modified the basic sample which I assume you were working from to demonstrate this:
class LayoutsContainer extends React.Component {
open() {
$("#win1 [data-role='window']").data('kendoWindow').open();
}
onOpen(){
console.log('Open event was triggered!');
}
onClose(){
console.log('Close event was triggered!');
}
onDragStart(){
console.log('DragStart event was triggered!');
}
onDragEnd(){
console.log('DragEnd event was triggered!');
}
render() {
return (
<div id="win1">
<Window open={this.onOpen} close={this.onClose} dragstart={this.onDragStart} dragend={this.onDragEnd} title={"About Alvar Aalto"} width={"640px"}>
<p>Alvar Aalto is one of the greatest names in modern architecture and design. Glassblowers at the iittala factory still meticulously handcraft the legendary vases that are variations on one theme, fluid organic shapes that let the end user decide the use. Interpretations of the shape in new colors and materials add to the growing Alvar Aalto Collection that remains true to his original design.</p>
<p>Born Hugo Alvar Henrik Aalto (February 3, 1898 - May 11, 1976) in Kuortane, Finland, was noted for his humanistic approach to modernism. He studied architecture at the Helsinki University of Technology from 1916 to 1921. In 1924 he married architect Aino Marsio.</p>
<p>Alvar Aalto was one of the first and most influential architects of the Scandinavian modern movement, and a member of the Congres Internationaux d'Architecture Moderne. Major architectural works include the Finlandia Hall in Helsinki, Finland, and the campus of Helsinki University of Technology.</p>
</Window>
<span id="undo" className="k-button" onClick={this.open}>Click here to open the Window</span>
</div>
);
}
}
ReactDOM.render(
<LayoutsContainer />,
document.querySelector('my-app')
);
I'm using react-testing-libarary to test my react application. For some reason, I need to be able to find the element by id and not data-testid. There is no way to achieve this in the documentation.
Is there a way to achieve this?
I have the rendered output as follows:
const dom = render(<App />);
I'm looking for something along the lines of:
const input = dom.getElementById('firstinput');
//or
const input = dom.getById('firstinput');
I feel like none of the answers really gave a complete solution, so here it is:
const result = render(<SomeComponent />);
const someElement = result.container.querySelector('#some-id');
I found a way to do this.
import App from './App';
import { render, queryByAttribute } from 'react-testing-library';
const getById = queryByAttribute.bind(null, 'id');
const dom = render(<App />);
const table = getById(dom.container, 'directory-table');
I hope this helps.
It looks you have DOM node itself as a container. Therefore, you should be able to call .querySelector('#firstinput') with that.
There are two ways to do so
Simply use container.getElementById('id'). In the end, all the helpers are doing is making queries like this one under the hood
If you want to have your custom query you can write a custom render. Check the documentation for more info https://github.com/kentcdodds/react-testing-library#getbytestidtext-textmatch-htmlelement
As a final note, if you can avoid looking for elements by id it's better.
You can set up with testIdAttribute in the configuration.
configure({ testIdAttribute: 'id' })
https://testing-library.com/docs/dom-testing-library/api-configuration
The setting has pros and cons. The benefit of it is that you can set an id for multiple uses. (Test id, marketing analytics, tag manager, ...etc) You don't have to add both id and test-id. It's good for the conciseness of the code.
But be careful, you might accidentally set the same id at two different components on the same page. Remember to add index or identification to a component id for list items.
My advice: stop adding and searching by ids, this always takes to much time and effort because you have to add the ids (sometimes test-ids) and then find out the best way to query the element. But even if you really need an id, this tool will save you a lot of time by showing the best way to query any DOM element on your screen: Testing Playground
If you use TypeScript, and want to get a non-null result, here's a convenience function:
function getById<T extends Element>(container: HTMLElement, id: string): T {
const element = container.querySelector<T>(`#${id}`);
assert(element !== null, `Unable to find an element with ID #${id}.`)
return element;
}
You can then use it like this:
import { render } from '#testing-library/react';
const { container } = render(<App />);
const myInputElement = getById<HTMLInputElement>(container, 'myInputElement');
Been stuck for a while now trying to figure this out and pulling my hair out because it should be simple but I can't seem to get my head around how to properly implement it.
I'm using GatsbyJS with Contentful as a CMS.
I want to have a simple way for admins using Contentful to add new documents and for those documents to show up on the site under the sections (also added by the admin).
So essentially admins will see a section such as "Minutes Documents" and under that, they'd be able to add first a new Year section such as 2018 then under that section a corresponding document (link to PDF file) under the title of the Month that document was released such as January.
On the site, I'd like that to display like so for instance:
Minutes Documents
2018
Jan
Feb
etc...
2017
Jan
Feb
etc...
So the admin can add as many Year Sections as they need and within that 12 files titled by Month.
The furthest I've managed to get so far is being able to display an array of all my documents added, but I can't yet get my head around how I can best split divide them up under section headers for each year.
My current code is like so:
GrahpQL document query:
export const query = graphql`
query NMODocs {
allContentfulDocument {
edges {
node {
id
publishDate(formatString: "MMMM, YYYY")
title
file {
file {
url
}
}
}
}
}
}
`;
I have an array:
<ul>
{data.allContentfulDocument.edges.map(({ node }) => (
<DocListing key={node.id} doc={node} />
))}
</ul>
And a DocLising component:
import React from 'react';
const DocListing = ({ doc }) => (
<li>
<a title={doc.title} href={doc.file.file.url}>
{doc.publishDate}
</a>
</li>
);
export default DocListing;
This currently results in a list of dates that link to corresponding document files. So to reiterate my question I'm wondering the best way to display this data under the relevant year section headings?
Apologies if this is poorly explained as I'm still learning and trying to get to grips with a lot of stuff that's new to me on this project. So I'm open to any suggestions or advice on how to approach this.
Assuming that you have an array with accessible dates you could group them with a little helper function.
const list = ['2018-08-12', '2018-07-12', '2017-08-12'];
const groupedList = list.reduce((acc, item) => {
const year = item.split('-')[0];
if (!acc[year]) {
acc[year] = []
}
acc[year].push(item);
return acc;
}, {});
Which would result in an object including keys for every year.
I'm not sure this possible with a GraphQL query.
Also in case you're relying on the publish date in contentful that's a little risky because I think it changes when content is updated to the last publish date (please double check). It might make sense to split it into its own content type so that you can set the date yourself and be sure.
Hope that helps. :)