@equinor/echo-components
v0.11.18
Published
This publishable library was generated with [Nx](https://nx.dev).
Downloads
768
Maintainers
Keywords
Readme
EchoComponents
This publishable library was generated with Nx.
Building blocks for Echo, built on top of EDS (Equinor Design System).
Guidelines for storybook
Think of Storybook as a virtual showroom for your app's building blocks - the UI components. It lets developers experiment with components in isolation, like tinkering with Legos before building something bigger. Storybook isn't just about documenting components, it's about creating an interactive playground for developers to explore their full potential.
Online storybook
Publishing static storybook page
- a github action builds and deploys the static storybook page to the following branch:
echo-components-storybook
- we shouldn't delete this one! - another github action will pick it up from here, and publish it to a github page.
Online storybook can be found here
Why Use Storybook?
- Faster Development: Experiment with components quickly, without needing the whole app running.
- Improved Consistency: Ensures all components look and behave the same way throughout the app.
- Better Collaboration: Designers and developers can easily share component design and functionality.
- Easier Maintenance: New developers can understand existing components through clear documentation.
Before you write your storybook
- Understand Your Components: Take some time to thoroughly understand the components you'll document. What purpose do they serve? What props do they accept? What states can they be in? This knowledge will guide the scenarios you showcase in your storybook.
- Know Your Audience: Consider who will be using your storybook. Are they primarily developers new to the codebase, or seasoned veterans? This will influence the level of detail you provide and the complexity of the stories you create.
Building Blocks of Captivating Stories
- Isolate and Conquer: Storybook lets components shine by themselves. This allows developers to experiment with props and states without worrying about interactions with other parts of the code.
- Variety is the Spice of Life: Don't just show the basics! Showcase the versatility of your component with different prop combinations (sizes, styles), visual themes (light/dark mode), and functional states (loading, error, success).
- Interactive Playground: Utilize Storybook's interactivity features. Allow developers to change props and states directly within the storybook interface. This fosters exploration and experimentation.
Writing Effective Descriptions:
- Clear and Concise: Write clear and concise descriptions that explain the component's purpose and usage. Avoid technical jargon that new developers might not understand.
- Focus on Benefits: Highlight the benefits of using the component. How does it make development easier or more efficient?
- Code Examples: Provide relevant code examples that demonstrate how to use the component in practice. Include both basic usage and more advanced scenarios.
Beyond the Basics:
- Accessibility for All: Consider accessibility in your stories. Showcase how the component can be used to create interfaces that everyone can use.
- BeBest Practices for Best Results: Include examples of how to use the component effectively. This helps developers avoid common pitfalls and write cleaner, more maintainable code.
- Visual Appeal Matters: Use clear and organized layouts for your stories. Consider using visual aids like screenshots or mockups to enhance understanding.
Text Example: Let's Build a Button Story!
Imagine a button component - the workhorse of applications. Here's how to craft a compelling story around it:
- Title: Button - A Versatile Action Trigger
- Description: A versatile button component for triggering actions in your application. Supports various styles (primary, secondary), sizes (large, small), and states (enabled, disabled) for a consistent user experience.
- Basic Usage: Show a simple button with its default label and style.
- Prop Variations: Include stories that demonstrate different props like size, variant, and disabled state.
- Visual Themes: Showcase the button in different themes (light mode, dark mode) if applicable.
- Interactive Playground: Allow developers to change the button text, choose a variant, and toggle the disabled state directly within the storybook.
- Code Example: Provide a code snippet demonstrating how to import and use the button component with different props.
Recipe for Writing a Good Story in Your Storybook (Using the Button Component Example)
Ingredients:
- Fresh Code: The code for the component you want to showcase (like the provided Button.stories.tsx file).
- Storybook Utilities: StoryFn and Meta functions from @storybook/react.
- Clarity and Conciseness: Aim for clear and easy-to-understand stories.
- Variety: Showcase different configurations and usage scenarios.
- Visual Appeal (Optional): Make the stories visually appealing for better engagement.
Step 1. Set the Stage: Configuration and Title (Meta): Define the Meta object with:
- title: A descriptive title indicating the component hierarchy (e.g., "Components/Button/Button").
- component: The component you're showcasing (e.g., Button).
- args: Default values for component props (e.g., as: 'button').
- argTypes: Define control types for interactive prop selection.
- parameters.docs.page: Link to your component's documentation (optional).
import { Meta, StoryFn } from '@storybook/react';
import { Button } from './Button'; // Replace with your Button component import path
const meta: Meta<typeof Button> = {
title: 'Components/Button/Button', // Descriptive title for Storybook navigation
component: Button,
args: {
as: 'button', // Default element type (can be overridden as 'span' or 'a')
},
argTypes: {
as: {
options: ['span', 'a', 'button'], // Available options for the 'as' prop
control: {
type: 'select', // Control type for selecting the 'as' prop in Storybook UI
},
},
},
parameters: {
docs: {
page: './Button.docs.mdx', // Path to your button documentation file (replace if needed)
},
},
};
export default meta;
Step 2. Create the Template: Write a reusable Template function that renders the component within a container element (e.g., div). This template will be used in most stories.
const Template: StoryFn<Button.Props> = (args) => (
<div className="button-container"> {/* Add a container class for styling (optional) */}
<Button {...args} />
</div>
);
Step 3. Tell the Main Story: Introduction:
- Define a story named Introduction (or similar) that uses the Template function.
- Render a basic button with its default props to showcase its fundamental form.
export const Introduction: StoryFn<Button.Props> = () => (
<Template>You can control me</Template>
);
Step 4. Spice it Up: Variations and States: Create additional stories to demonstrate different button variations and states. These could be:
- Different styles (primary, secondary, outlined)
- Sizes (large, small)
- Disabled state
- Loading state (if applicable)
- Icon variations (with or without icons)
- Interactive elements (toggling loading state) For each story, provide a descriptive title that reflects the variation (e.g., "Primary Button - Large").
export const PrimaryButtonLarge: StoryFn<Button.Props> = () => (
<Template>
<Button variant="primary" size="large">
Large Primary Button
</Button>
</Template>
);
export const DisabledButton: StoryFn<Button.Props> = () => (
<Template>
<Button disabled>Click Me (Disabled)</Button>
</Template>
);
export const LoadingButton: StoryFn<Button.Props> = () => (
<Template>
<Button isLoading>Loading...</Button>
</Template>
);
export const IconButton: StoryFn<Button.Props> = () => (
<Template>
<Button variant="icon">
<Icon name="search" /> {/* Assuming you have an Icon component */}
</Button>
</Template>
);
Step 5. Visual Presentation (Optional): Consider using background colors or layouts to enhance the visual appeal of your stories. Example: You can customize the button-container class in the Template to add background colors or styles for a more visually appealing presentation.
Step 6. Serve it Up! (Export): Export the meta object (containing configuration) at the end of your file..
export default meta;
Picture Example: SecondaryMarker
How to develop and release EchoComponents
Check the readme in the NX libraries folder.
Available NPM scripts
lint-components
build-components
test-components
Old repository of echoComponents
Keeping it for historical reasons. https://github.com/equinor/EchoComponents
Guidelines for developing
Composition
When deciding how to write your new component consider the composition. Using smaller interchangeable components that each represent a specific function or feature in the context of a larger component. Using wrapper components in conjunction with the children
property, allows for a greater separation of concerns, smaller blocks of code and a greater level of customization in the final product.
Example: Card Component
<Card.Container> {/* REQUIRED */}
<Card.Image src="hello_world.jpg"> {/* INTERCHANGEABLE */}
<Card.Caption>Descriptive text</Card.Caption> {/* INTERCHANGEABLE */}
<Card.Heading>Heading</Card.Heading> {/* INTERCHANGEABLE */}
<Card.Ingress>Subtitle/Ingress</Card.Ingress> {/* INTERCHANGEABLE */}
<Card.Article>Main content</Card.Article> {/* INTERCHANGEABLE */}
</Card.Image>
</Card.Container>
The makeup of each component may vary depending on the feature and the problem being solved. Some components may not be interchangeable, and some components may need additional props.
Abstracting the individual composition should be possible, by creating a new component in the project that can act as a bootstrapping mechanism for the EchoComponent composition.
Example: Bootstrapping Card Components
import React from 'react';
type Props = {
heading: string;
mainContent: string;
imageSrc?: string;
imageCaption?: string;
ingress?: string;
};
export const ExampleCard: React.FC<Props> = ({ heading, mainContent, imageSrc, imageCaption, ingress }) => {
return (
<Card.Container>
<Card.Image src={imageSrc}>
{imageCaption && <Card.Caption>{imageCaption}</Card.Caption>}
<Card.Heading>{heading}</Card.Heading>
{ingress && <Card.Ingress>{ingress}</Card.Ingress>}
<Card.Article>{mainContent}</Card.Article>
</Card.Image>
</Card.Container>
);
};
export default ExampleCard;
Developer Checklist
- Keep components small, focused with a single responsibility where possible.
- When possible, use wrapper components with properties for
children
andclassName
. className
should always be included, to allow for custom styling when needed. (see point 6. for more information on custom styling)- Styling of components should always be done with CSS Modules.
- Make sure the component(s) accurately reflect the provided UX sketches, but do not be afraid to challenge the UX sketches if they have not accounted for specific uses case, and/or present a unusually difficult challenge.
- Allowing custom styling of individual components does not mean developers have free rein to do as they please. Custom styling should always be approved by UX and only when it makes sense for the user experience.
- When naming components always check if the UX sketches already have a name, if not make the name as intuitive and descriptive as possible.
- Add each component to Storybook with default props, examples, and explanations (when needed).