Storybook conversion to CSF - javascript

In the process of converting React Stories to use the advised CSF
I'm wondering how can the following (stories that are generated from looping an array of objects) be converted to CSF?
ps: my real array has over 15 items. this is just an example below
const stories = storiesOf("MyStories", module)
const storyOptions = [{name: "foo", age:21}, {name: "bar", age: 22},{name: "batman", age: 23}, ] //
for (let option of storyOptions) {
stories.add(option.name, () => {
return <MyWrapperComponent option={option} />
})
}
I'm having difficulties refactoring the above to output the same multiple stories as CSF:
Only the first story returns so far:
export const myStories = () => {
for (let option of storyOptions) {
return <MyWrapperComponent option={option} />
}
}

With Storybook 5.x, the latest version available as I'm writing this, it's not possible to iterate over an array and dynamically generate stories with CSF format because the requirements to have working stories are strict:
- a required default export
- one or more named exports
Everything is fine with the default export however there is currently no way to have dynamic named exports because JS importand export have to be statically analyzable - i.e. known before executing the code. Unfortunately, you are stuck with the old syntax for now.

Related

how to use localstorage in to do list project

Is this the correct way to store projects and tasks with localstorage? I also need to get the localstorage every time the page refreshes. So how do I do that?
export function newProject(name) {
allProjects.push({
projectTitle: name,
id: crypto.randomUUID(),
tasks: []
})
getProjectId(name)
save(name, project)
}
export function save(title, task) {
localStorage.setItem(title, JSON.stringify(task))
}
project is undefined so you need to define it first.
const project = {
projectTitle: name,
id: crypto.randomUUID(),
tasks: []
}
allProjects.push(project)
getProjectId(name)
save(name, project)
to get all projects on refresh, you need to maintain an array of names in localstorage, or save all projects as array in one key.
EDIT:
So the question is not entirely well described, but i did my best. Add exports to functions you need and use your getProjectId if needed. I used an approach with separate array of ID's to maintain the list of projects.
function createNewProject(name) {
// create and return project object
return {
title: name,
id: crypto.randomUUID(),
tasks: []
};
}
function saveProject(storageKey, projectObject) {
// get current list of project keys or create new list
const allProjectKeys = JSON.parse(localStorage.getItem("allProjectKeys")) ?? [];
// add new one to the list
allProjectKeys.push(storageKey);
// save current list of project keys
localStorage.setItem("allProjectKeys", JSON.stringify(allProjectKeys));
// save project data
localStorage.setItem(storageKey, JSON.stringify(projectObject));
}
function getProjectByKey(storageKey) {
// get single project by given key
return JSON.parse(localStorage.getItem(storageKey));
}
function getAllProjects() {
// get list of all project keys, and map each of them to get actual project instead of project key
return JSON.parse(localStorage.getItem("allProjectKeys")).map(getProjectByKey);
}
const testProject = createNewProject("test");
saveProject(testProject.id, testProject);
console.log(getProjectByKey(testProject.id));
console.log(getAllProjects());

Import Multiple images and export array

Sorry if it seems a bit confusing, I'll try to be as simple and put as much demonstration as possible.
The problem
I have a file that needs to import a bunch of images (for now 65 images). In order not to be a manual process, even when adding a new image in the folder and already being 'automatically detected' I created a function to import everything. However, I need to 'export' this function into an object like this:
const brandsAdds = [
{
label: "brand1",
value: "brand1",
logo: Object,
},
{
label: "brand2",
value: "brand2",
logo: Object,
},
...
];
How am I importing all the images:
// Import all images in folder
function importAll(r) {
let images = {};
r.keys().map((item, index) => {
images[item.replace("./", "").slice(0, -4)] = r(item).default;
});
return images;
}
// Variable for easy access to data
const image = importAll(
require.context("../", false, /\.(png|jpe?g|svg|gif)$/)
);
Demo
Automated process:
https://codesandbox.io/s/frosty-sun-n75j5?file=/pages/index.js
Result:
Print result: what must be converted and how
Manual process:
https://codesandbox.io/s/frosty-sun-n75j5?file=/pages/index_manual.js
Result:
Print result manual process
I'm using next.js and this file that will export an array object will be used in another component. The file is currently over 300 lines long as I'm calling it image by image.
Would there be any way to import all the images by adding them to the array with "label:" and "value:" with the filename and "logo:" with the image path (next.js)?

Combining / nesting subscription to multiple collections in Firebase

I'm looking for a way to fetch data from multiple collections in a subscription.
I have two collections: inspection_processes and inspections. Each inspection belongs to exact one inspection_process. Each inspection contains data (foreign keys) from another collection called devices.
My goal is to get a list of all inspection_processes and fetch their respective inspections and the devices specified in each inspection.
Here's the data from Firestore (displayed JSON-like for better readability:
inspection_processes = [{
id: 'AAA',
process_name: 'P-001'
}]
inspections = [{
inspection_id: 'BBB',
inspection_process_id: 'AAA',
device_id: 'CCC'
}]
devices = [{
device_id: 'DDD',
device_name: 'Computer'
}]
My current (simplified) approach:
fetchInspectionProcesses() {
let inspecion_processes_data = [];
// Get inspection processes
this.getInspectionProcesses().subscribe(inspecion_processes => {
for (let inspection_process of inspecion_processes) {
// Get inspections of inspection process
this.getInspectionsByProcessId(inspection_process['id']).subscribe(inspections => {
for (let inspection of inspections) {
// Get device info
this.getDeviceById(inspection['device_id']).subscribe(device => {
// ... push data to inspecion_processes_data array
})
}
})
}
})
}
I know that this approach isn't recommended since it leads to different problems (duplicate output in the UI, memory leaks, ...).
I did my best trying to solve it by using RxJS (e.g. by piping) but I didn't manage to use the right operators. The problem is not limited to having two or three collections to combine, I'm looking for a general approach on how to do this.
Working with duplicate data (e.g. putting the device's name in the inspections document isn't an option since my example is just simplified, there are many nestings I have to deal with.
EDIT: Screenshot of the Firebase Console
Can anyone help, please? Thank you in advance!
What you're talking about is basically a join in firestore. You should really duplicate your data so you don't have to do this. I wrote some functions to do this in my package
https://github.com/jdgamble555/adv-firestore-functions#Aggregation
But you could also do SQL like joins (but you will incur more reads):
https://fireship.io/lessons/firestore-joins-similar-to-sql/
J
This is how you might remove your nested subscriptions
fetchInspectionProcesses() {
let inspecion_processes_data = [];
this.getInspectionProcesses().pipe(
map(inspecion_processes => inspecion_processes.map(
inspection_process => this.getInspectionsByProcessId(inspection_process['id'])
)),
mergeMap(processes => merge(...processes)),
map(inspections => inspections.map(
inspection => this.getDeviceById(inspection['device_id'])
)),
mergeMap(devices => merge(...devices)),
toArray()
).subscribe(deviceArray => {
// ... This is your inspecion_processes_data array
});
}
Or, a little bit more compactly by combining map and mergeMap together:
fetchInspectionProcesses() {
let inspecion_processes_data = [];
this.getInspectionProcesses().pipe(
mergeMap(inspecion_processes => merge(...inspecion_processes.map(
inspection_process => this.getInspectionsByProcessId(inspection_process['id'])
))),
mergeMap(inspections => merge(...inspections.map(
inspection => this.getDeviceById(inspection['device_id'])
))),
toArray()
).subscribe(deviceArray => {
// ... Do something with deviceArray
});
}

How do you create edgeless graphql element in Gatsby?

The title may be miss leading but I'm not really sure how do I ask this question correctly. Here is the problem: I'd like to query my own API(not created yet so I made placeholder data) for global settings which might change in the future and I will only need to rebuild the website instead of editing it manually, I want to create source node called CmsSettings and pass it to GraphQL (structure similar to site.siteMetadata) but I don't know how can I achieve that. What I achieved so far is to create a source node called allCmsSettings which has my data as an object in nodes array.
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const { createNode } = actions;
const myData = {
key: 123,
app_title: `The foo field of my node`,
...
}
const nodeContent = JSON.stringify(myData);
const nodeMeta = {
id: createNodeId(`my-data${ myData.key }`),
parent: null,
children: [],
internal: {
type: `CmsSettings`,
mediaType: `text/html`,
content: nodeContent,
contentDigest: createContentDigest(myData)
}
}
const node = Object.assign({}, myData, nodeMeta);
createNode(node);
}
Here is the query used to get the data of the source node
allCmsSettings {
edges {
node {
id
app_title
...
}
}
}
Creating a query results in an array of results(which I know is the result of creating source nodes) but I'd like to create that source so that I could query it like this and:
CmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
You get the point. I was browsing the gatsby node API but I can't find how to achieve this.
Thank you for your help
Nevermind, the answer is pretty simple, if you are new to gatsby just like me the sourceNodes export creates 2 graphql fields for you with all prefix and camel case source node. The thing that I wanted to make is already there and is queryable with
cmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
Notice the lowercase letter even though it was declared as CmsSettings. It seems that gatsby really does some magic under the hood.

React Native - Dynamic Image Source

I'm trying to loop through the SOURCE array with the map method, but I keep getting this error:
Unknown named module: '../images/one.jpeg'
Anyone know why this is happening? The file path in the require is definitely correct.
var SECTIONS = [
{
title: 'One',
fileName: 'one.jpeg',
},
{
title: 'Two',
fileName: 'two.jpeg',
},
{
title: 'Three',
fileName: 'three.jpeg',
},
{
title: 'Four',
fileName: 'four.jpeg',
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={require(`../images/${section.fileName}`)}
title={section.title}
/>
))}
I don't think this is possible because react native needs to know what to bundle ahead of time (AFAIK). However, you can require all the files in your array:
var SECTIONS = [
{
title: 'One',
file: require('../images/one.jpeg'),
},
{
title: 'Two',
file: require('../images/two.jpeg'),
},
{
title: 'Three',
file: require('../images/three.jpeg'),
},
{
title: 'Four',
file: require('../images/four.jpeg'),
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}
You can't use dynamic links. The best hack that i found to solve this is this:
var SECTIONS = {
One: {
title: 'One',
file: require('../images/one.jpeg'),
},
Two: {
title: 'Two',
file: require('../images/two.jpeg'),
},
Three: {
title: 'Three',
file: require('../images/three.jpeg'),
},
Four: {
title: 'Four',
file: require('../images/four.jpeg'),
},
};
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}
That way, you can just use the files and if you have some kind of dynamic image selection, you can just use something like this
<Image source={SECTIONS[image.type]} />
try opening the file in separate browser using direct URL something like
http://<><>/imgages/one.jpg
You can also do something like this as well:
One working example for displaying dynamic images using react :
Example Click Here
Got a working solution, though not recommended for large images, works perfectly for (a lot of)small images.
Steps:
Convert the icon(s) to base64 string(s).
Create a JSON file with filename as the keys and the base64 strings as values.
(You can also store them to a local database)
e.g.
ImageData.json
{
"icon1": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......==",
"icon2": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......=="
}
3.Import the json file to the place where you require the images dynamically.
e.g.
const imageData = require("./images/ImageData.json")
4: Get/generate the key/filename at runtime. and get the image source.
e.g.
const imageSrc = imageData[keyname]
5: Generate a image dynamically at runtime.
e.g.
<Image style={{ width: 70, height: 70, resizeMode: Image.resizeMode.contain }} source={ uri: imageSrc } />
Done..
Extra..
Written a helper python script to automate the json file creation.
import base64
import os
directory = os.fsencode('.')
with open('ImagesData.json', 'wb') as jsonFile:
jsonFile.write(bytes('{', 'utf-8'))
written = False
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith('.png'):
with open(filename, "rb") as image_file:
if written:
jsonFile.write(bytes(',\n','utf-8'))
encoded_string = base64.b64encode(image_file.read())
jsonFile.write(bytes(('"' +filename+ '":'), 'utf-8'))
jsonFile.write(bytes('"data:image/png;base64,', 'utf-8') + encoded_string + bytes('"', 'utf-8'))
written = True
jsonFile.write(bytes('}', 'utf-8'))
Copy the script to the image folder and run the script (requires python 3.6).
A json file will the created with image name as key and base64 string as values.
Copy the file to project and use (You can delete the images after that).
Use the json file as mentioned above.
I had the same problem but my situation was a little different. I had an array of different objects that needed dynamic images. I was already mapping the array, but I needed to match the images to that array based off of name. It was a little hacky, but this is how I went about it.
First, in my parent component I created a function to render a component for my array of objects. I passed the objects data into a prop called "object".
In my case I knew what my data was and I needed to match the corresponding image to the object that was being pulled off of an external api that I was grabbing my data from.
renderObjects() {
return this.state.objects.map(object => (
<ObjectListDetail
key={object.id}
next
props={this.props}
object={object}
/>
));
}
In my ObjectListDetail component, I created a variable called icons, which was another array of objects. This time, I created a name property that would match the object being passed to the component from the parent and then had a second key called source in which I provided the path to the image. It went something like this.
var icons = [
{ name: "BTC", source: Images.btc },
{ name: "ETH", source: Images.eth },
{ name: "ETC", source: Images.etc },
{ name: "ZRX", source: Images.zrx },
{ name: "USDC", source: Images.usdc },
{ name: "LTC", source: Images.ltc },
{ name: "BCH", source: Images.bch },
{ name: "USD", source: Images.usd }
];
NOTE *** I had already required all of my images into a separate file for my entire app and imported them at the top.
I then created a variable called imgSrc and filtered the result to match the name of the object i was passing to the child component.
var imgSrc = icons.filter(
icon => icon.name === props.wallet.name
)
I then created an Image component and in the source requirement I called the result of the filter and pointed it to the source.
<Image source={imgSrc[0].source} />
That is how I achieved dynamic image rendering within my application.
Its probably not the best way to do things, and I am still kinda new at this, but I would love any criticism

Categories