Skip to content

[ REFACTOR ] : feature grid #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 172 additions & 105 deletions src/components/FeatureGrid/FeatureGrid.js
Original file line number Diff line number Diff line change
@@ -1,112 +1,179 @@
import React from 'react';
import { ArrowPathIcon, CloudArrowUpIcon, LockClosedIcon } from '@heroicons/react/20/solid';
import React from "react";
import PropTypes from "prop-types";

const features = [
{
name: 'Multicloud Deployment',
description:
'Seamlessly deploy and manage infrastructure across AWS, Google Cloud, and Azure, all from a single platform.',
href: '#',
icon: CloudArrowUpIcon,
},
{
name: 'Observability',
description:
'Built-in monitoring and visibility with integrated tools like Prometheus, Grafana, and FluentBit, ensuring real-time insights and log management.',
href: '#',
icon: LockClosedIcon,
},
{
name: 'In-Built Tracing Support',
description:
'Native support for distributed tracing, enabling detailed tracking and optimization of application performance across microservices.',
href: '#',
icon: ArrowPathIcon,
},
{
name: 'Container Image Management',
description:
'Automatically sets up container registries, handles permissions, and manages credentials for deploying images within your cluster.',
href: '#',
icon: CloudArrowUpIcon,
},
{
name: 'Service Configurations',
description:
'Manages service settings by handling YAML configurations automatically and requiring only essential input from you.',
href: '#',
icon: LockClosedIcon,
},
{
name: 'Secure Datastore Connections',
description:
'Simplifies database setup, including creating datastores, managing firewalls, and securely attaching credentials to service pods.',
href: '#',
icon: ArrowPathIcon,
},
// {
// name: 'Ingress Management',
// description:
// 'Automatically configures host attachments and manages TLS certificates to securely expose your services to the internet.',
// href: '#',
// icon: CloudArrowUpIcon,
// },
// {
// name: 'Permission Management',
// description:
// 'Streamlines user permission setup by allowing you to assign RBAC using email addresses, simplifying access control.',
// href: '#',
// icon: LockClosedIcon,
// },
];
/**
* @typedef {Object} ClassNames
* @property {string} [root] - Class name for the root container.
* @property {string} [container] - Class name for the main container.
* @property {string} [textContainer] - Class name for the text container.
* @property {string} [title] - Class name for the title.
* @property {string} [heading] - Class name for the heading.
* @property {string} [subheading] - Class name for the subheading.
* @property {string} [featuresContainer] - Class name for the features container.
* @property {string} [featuresGrid] - Class name for the features grid.
* @property {string} [featureItem] - Class name for each feature item.
* @property {string} [featureName] - Class name for the feature name.
* @property {string} [featureIcon] - Class name for the feature icon.
* @property {string} [featureDescription] - Class name for the feature description.
* @property {string} [featureLink] - Class name for the feature link.
*/

export default function FeatureGrid({ title, heading, subheading, ...props }) {
return (
<div className="bg-gray-50 py-24 mt-8 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:text-center">
<h2 className="text-base font-semibold leading-7 text-primary-600">{title}</h2>
<p className="mt-2 text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
{heading}
</p>
<p className="mt-6 text-lg leading-8 text-gray-600">
{subheading}
</p>
</div>
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-none">
<dl className="grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 lg:max-w-none lg:grid-cols-3">
{features.map((feature) => (
<div key={feature.name} className="flex flex-col">
<dt className="flex items-center gap-x-3 text-base font-semibold leading-7 text-gray-900">
<feature.icon aria-hidden="true" className="h-5 w-5 flex-none text-primary-600" />
{feature.name}
</dt>
<dd className="mt-4 flex flex-auto flex-col text-base leading-7 text-gray-600">
<p className="flex-auto">{feature.description}</p>
<p className="mt-6">
{/* <a
href={feature.href}
className="text-sm font-semibold leading-6 text-indigo-600"
>
Learn more <span aria-hidden="true">→</span>
</a> */}
</p>
</dd>
</div>
))}
</dl>
</div>
</div>
/**
* @typedef {Object} Feature
* @property {string} name - The name of the feature.
* @property {string} description - The description of the feature.
* @property {React.ComponentType<React.SVGProps<SVGSVGElement>>} icon - The React component for the feature icon.
*/

/**
* @typedef {{
* title: string,
* heading: string,
* subheading?: string,
* features: Feature[],
* classNames?: ClassNames,
* variant?: "GRID" | "ROW-LEFT" | "ROW-RIGHT"
* }} FeatureGridProps
*/

/**
* @param title
* @param heading
* @param subheading
* @param features
* @param classNames
* @param variant
* @param {FeatureGridProps} props - The component props.
* @returns {JSX.Element}
*/

/**
* FeatureGrid component for displaying a grid of features.
*/

export default function FeatureGrid({
title,
heading,
subheading,
features,
classNames = {},
variant = "GRID",
...props
}) {
let defaultClassNames = {
root: "bg-gray-50 py-24 mt-8 sm:py-32",
container: "mx-auto max-w-7xl px-6 lg:px-8",
textContainer: "mx-auto max-w-2xl lg:text-center",
title: "text-base font-semibold leading-7 text-primary-600",
heading: "mt-2 text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl",
subheading: "mt-6 text-lg leading-8 text-gray-600",
featuresContainer:
"mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-none",
featuresGrid:
"grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 lg:max-w-none lg:grid-cols-3",
featureItem: "flex flex-col",
featureName:
"flex items-center gap-x-3 text-base font-semibold leading-7 text-gray-900",
featureIcon: "h-5 w-5 flex-none text-primary-600",
featureDescription:
"mt-4 flex flex-auto flex-col text-base leading-7 text-gray-600",
featureLink: "text-sm font-semibold leading-6 text-indigo-600",
};

switch (variant) {
case "GRID":
defaultClassNames = {
...defaultClassNames,
};
break;

case "ROW-RIGHT":
defaultClassNames = {
...defaultClassNames,
root: "bg-gray-50 py-10",
featuresGrid: "flex py-6 flex-wrap gap-4 md:min-w-100",
featureItem: "flex flex-col min-w-52 max-w-64 flex-1 p-2 rounded-md",
container: "flex flex-col gap-2 p-8 md:flex-row",
textContainer: "text-center md:w-2/3 md:text-left",
featuresContainer: "w-full",
};
break;

case "ROW-LEFT":
defaultClassNames = {
...defaultClassNames,
root: "bg-gray-50 py-10",
featuresGrid: "flex py-6 flex-wrap gap-4 md:min-w-100",
featureItem: "flex flex-col min-w-52 max-w-64 flex-1 p-2 rounded-md",
container: "flex flex-col gap-2 p-8 md:flex-row-reverse",
textContainer: "text-center md:w-2/3 md:text-left",
featuresContainer: "w-full",
};
break;
}

const formattedClassNames = {
...defaultClassNames,
...Object.keys(classNames).reduce((acc, className) => {
acc[className] =
`${defaultClassNames[className]} ${classNames[className]}`;
return acc;
}, {}),
};

return (
<div className={formattedClassNames.root} {...props}>
<div className={formattedClassNames.container}>
<div className={formattedClassNames.textContainer}>
<h2 className={formattedClassNames.title}>{title}</h2>
<p className={formattedClassNames.heading}>{heading}</p>
<p className={formattedClassNames.subheading}>{subheading}</p>
</div>
);
<div className={formattedClassNames.featuresContainer}>
<dl className={formattedClassNames.featuresGrid}>
{features.map((feature) => (
<div
key={feature.name}
className={formattedClassNames.featureItem}
>
<dt className={formattedClassNames.featureName}>
<feature.icon
aria-hidden="true"
className={formattedClassNames.featureIcon}
/>
{feature.name}
</dt>
<dd className={formattedClassNames.featureDescription}>
<p className="flex-auto">{feature.description}</p>
</dd>
</div>
))}
</dl>
</div>
</div>
</div>
);
}

FeatureGrid.propTypes = {
/** Smaller text at the top of component - couple of words */
title: PropTypes.string,
/** Large heading - ideally a single statement */
heading: PropTypes.string,
/** Small heading - can be descriptive */
subheading: PropTypes.string,
};
/** Smaller text at the top of component - a couple of words */
title: PropTypes.string.isRequired,
/** Large heading - ideally a single statement */
heading: PropTypes.string.isRequired,
/** Small heading - can be descriptive */
subheading: PropTypes.string,
/** Array of feature objects */
features: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
icon: PropTypes.elementType.isRequired,
// Uncomment if you want to include href in the feature object
// href: PropTypes.string,
}),
).isRequired,
/** Custom class names for overriding default styles */
classNames: PropTypes.object,

variant: PropTypes.oneOf(["GRID", "ROW-LEFT", "ROW-RIGHT"]),
};
104 changes: 89 additions & 15 deletions src/components/FeatureGrid/FeatureGrid.stories.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,95 @@
import FeatureGrid from "./FeatureGrid";
import {fn} from "@storybook/test";
import { fn } from "@storybook/test";
import {
ArrowPathIcon,
CloudArrowUpIcon,
LockClosedIcon,
} from "@heroicons/react/20/solid";

export default {
title: 'Marketing/FeatureGrid',
component: FeatureGrid,
argTypes: {
title: { control: 'text' },
heading: { control: 'text' },
subheading: { control: 'text' },
},
tags: ['autodocs'],
title: "Marketing/FeatureGrid",
component: FeatureGrid,
argTypes: {
title: { control: "text" },
heading: { control: "text" },
subheading: { control: "text" },
},
tags: ["autodocs"],
};

const features = [
{
name: "Multicloud Deployment",
description:
"Seamlessly deploy and manage infrastructure across AWS, Google Cloud, and Azure, all from a single platform.",
href: "#",
icon: CloudArrowUpIcon,
},
{
name: "Observability",
description:
"Built-in monitoring and visibility with integrated tools like Prometheus, Grafana, and FluentBit, ensuring real-time insights and log management.",
href: "#",
icon: LockClosedIcon,
},
{
name: "In-Built Tracing Support",
description:
"Native support for distributed tracing, enabling detailed tracking and optimization of application performance across microservices.",
href: "#",
icon: ArrowPathIcon,
},
{
name: "Container Image Management",
description:
"Automatically sets up container registries, handles permissions, and manages credentials for deploying images within your cluster.",
href: "#",
icon: CloudArrowUpIcon,
},
{
name: "Service Configurations",
description:
"Manages service settings by handling YAML configurations automatically and requiring only essential input from you.",
href: "#",
icon: LockClosedIcon,
},
{
name: "Secure Datastore Connections",
description:
"Simplifies database setup, including creating datastores, managing firewalls, and securely attaching credentials to service pods.",
href: "#",
icon: ArrowPathIcon,
},
];

export const Primary = {
args: {
title: "Deploy faster",
heading: "Everything you need to deploy your app",
subheading: 'Streamline your infrastructure provision process with user-friendly tools and automated updates. Get your app to users quickly and efficiently—focus on building, while we handle the rest!',
},
};
args: {
title: "Deploy faster",
heading: "Everything you need to deploy your app",
subheading:
"Streamline your infrastructure provision process with user-friendly tools and automated updates. Get your app to users quickly and efficiently—focus on building, while we handle the rest!",
features: features,
},
};

export const RightHandedText = {
args: {
title: "Deploy Faster",
heading: "Everything you need to deploy your app",
subheading:
"Streamline your infrastructure provision process with user-friendly tools and automated updates.",
features: features,
variant: "ROW-RIGHT",
},
};

export const LeftHandedText = {
args: {
title: "Deploy Faster",
heading: "Everything you need to deploy your app",
subheading:
"Streamline your infrastructure provision process with user-friendly tools and automated updates.",
features: features,
variant: "ROW-LEFT",
},
};