diff --git a/.github/workflows/kops-deploy-stage.yaml b/.github/workflows/kops-deploy-stage.yaml
index a8dd974..a1605aa 100644
--- a/.github/workflows/kops-deploy-stage.yaml
+++ b/.github/workflows/kops-deploy-stage.yaml
@@ -6,7 +6,7 @@ on:
env:
APP_NAME: ui-components
- NAMESPACE: kops-dev-stg
+ NAMESPACE: zop-web-stage
CLUSTER_NAME: raramuri-tech
CLUSTER_PROJECT: raramuri-tech
GAR_PROJECT: raramuri-tech
diff --git a/nginx.conf b/nginx.conf
index 263e2ec..04792b9 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -6,15 +6,24 @@ events {
}
http {
+ include mime.types;
+
server {
- include mime.types;
- listen *:8000; # Listen for incoming connections from any interface on port 80
- server_name ""; # Don't worry if "Host" HTTP Header is empty or not set
- root /usr/share/nginx/html; # serve static files from here
+ listen *:8000; # Listen for incoming connections from any interface on port 8000
+ server_name ""; # Allow empty or not set "Host" HTTP Header
+ root /usr/share/nginx/html; # Serve static files from here
+
+ # Redirect `/ui-components` to `/ui-components/`
+ location = /ui-components {
+ return 301 $scheme://$host/ui-components/;
+ }
+ # Serve the React app under `/ui-components/`
location / {
- try_files $uri $uri/ /index.html;
+ try_files $uri $uri/ /index.html;
}
+
+ # Optional health check locations
# location /.well-known/health-check {
# access_log off;
# return 200 "healthy\n";
@@ -26,4 +35,4 @@ http {
# add_header Content-Type text/plain;
# }
}
-}
\ No newline at end of file
+}
diff --git a/public/index.html b/public/index.html
index 59023a8..8e29da4 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,10 +6,7 @@
-
+
-
React App
+ zopdev ui-component
diff --git a/src/components/BlogSection/BlogSection.js b/src/components/BlogSection/BlogSection.js
new file mode 100644
index 0000000..584f4eb
--- /dev/null
+++ b/src/components/BlogSection/BlogSection.js
@@ -0,0 +1,152 @@
+import React from "react";
+
+import PropTypes from "prop-types";
+
+function classNames(...classes) {
+ return classes.filter(Boolean).join(" ");
+}
+
+export default function BlogSection({
+ title,
+ subtitle,
+ posts = [],
+ variants = "grid-image",
+ orientation = "row",
+}) {
+ const containerClasses = classNames(
+ "bg-white py-20 sm:py-28",
+ orientation === "column" ? "max-w-2xl lg:max-w-4xl" : "max-w-7xl"
+ );
+
+ const headerClasses = classNames(
+ "text-balance text-4xl font-semibold tracking-tight text-gray-900 sm:text-5xl",
+ orientation !== "column" && "text-center"
+ );
+
+ const gridClasses = classNames(
+ orientation === "column"
+ ? "mt-10 space-y-16 border-t border-gray-200 pt-10 sm:mt-16 sm:pt-16"
+ : "mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-3",
+ variants === "grid-text" && "border-t border-gray-200"
+ );
+
+ return (
+
+
+
+
{title}
+ {subtitle && (
+
{subtitle}
+ )}
+
+
+ {posts?.map((post) => (
+
+ {variants === "grid-image" && (
+
+

+
+
+ )}
+
+
+
+
+
+ {post.description}
+
+
+
+

+
+
+
+
+ ))}
+
+
+
+ );
+}
+
+BlogSection.propTypes = {
+ title: PropTypes.string.isRequired,
+ subtitle: PropTypes.string,
+ posts: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ title: PropTypes.string.isRequired,
+ href: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ imageUrl: PropTypes.string.isRequired,
+ date: PropTypes.string.isRequired,
+ datetime: PropTypes.string.isRequired,
+ category: PropTypes.shape({
+ title: PropTypes.string.isRequired,
+ href: PropTypes.string.isRequired,
+ }).isRequired,
+ author: PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ role: PropTypes.string.isRequired,
+ href: PropTypes.string.isRequired,
+ imageUrl: PropTypes.string.isRequired,
+ }).isRequired,
+ })
+ ).isRequired,
+ variants: PropTypes.oneOf(["grid-image", "grid-text"]),
+ orientation: PropTypes.oneOf(["row", "column"]),
+};
+
+// ImageGrid.defaultProps = {
+// subtitle: "",
+// variants: "grid-image",
+// orientation: "row",
+// };
diff --git a/src/components/BlogSection/BlogSection.stories.js b/src/components/BlogSection/BlogSection.stories.js
new file mode 100644
index 0000000..a63b142
--- /dev/null
+++ b/src/components/BlogSection/BlogSection.stories.js
@@ -0,0 +1,97 @@
+import BlogSection from "./BlogSection";
+import { fn } from "@storybook/test";
+
+export default {
+ title: "Marketing/BlogSection",
+ component: BlogSection,
+ argTypes: {
+ title: { control: "text" },
+ subtitle: { control: "text" },
+ variants: {
+ control: {
+ type: "select",
+ options: ["grid-image", "grid-text"], // Define available options
+ },
+ description: "Choose between displaying a grid of images or text.",
+ },
+ orientation: {
+ control: {
+ type: "select",
+ options: ["row", "column"], // Define available options
+ },
+ description: "Set the layout orientation for the grid.",
+ },
+ },
+ tags: ["autodocs"],
+};
+
+const posts = [
+ {
+ id: 1,
+ title: "Boost your conversion rate",
+ href: "#",
+ description:
+ "Illo sint voluptas. Error voluptates culpa eligendi. Hic vel totam vitae illo. Non aliquid explicabo necessitatibus unde. Sed exercitationem placeat consectetur nulla deserunt vel. Iusto corrupti dicta.",
+ imageUrl:
+ "https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80",
+ date: "Mar 16, 2020",
+ datetime: "2020-03-16",
+ category: { title: "Marketing", href: "#" },
+ author: {
+ name: "Michael Foster",
+ role: "Co-Founder / CTO",
+ href: "#",
+ imageUrl:
+ "https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
+ },
+ },
+ {
+ id: 2,
+ title: "How to use search engine optimization to drive sales",
+ href: "#",
+ description:
+ "Optio cum necessitatibus dolor voluptatum provident commodi et. Qui aperiam fugiat nemo cumque.",
+ imageUrl:
+ "https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80",
+ date: "Mar 10, 2020",
+ datetime: "2020-03-16",
+ category: { title: "Sales", href: "#" },
+ author: {
+ name: "Michael Foster",
+ role: "Co-Founder / CTO",
+ href: "#",
+ imageUrl:
+ "https://images.unsplash.com/photo-1547586696-ea22b4d4235d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3270&q=80",
+ },
+ },
+ {
+ id: 3,
+ title: "Boost your conversion rate",
+ href: "#",
+ description:
+ "Illo sint voluptas. Error voluptates culpa eligendi. Hic vel totam vitae illo. Non aliquid explicabo necessitatibus unde. Sed exercitationem placeat consectetur nulla deserunt vel. Iusto corrupti dicta.",
+ imageUrl:
+ "https://images.unsplash.com/photo-1492724441997-5dc865305da7?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3270&q=80",
+ date: "Mar 16, 2020",
+ datetime: "2020-03-16",
+ category: { title: "Marketing", href: "#" },
+ author: {
+ name: "Michael Foster",
+ role: "Co-Founder / CTO",
+ href: "#",
+ imageUrl:
+ "https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
+ },
+ },
+ // More posts...
+];
+
+export const Primary = {
+ args: {
+ title: "From the blog",
+ subtitle: "Learn how to grow your business with our expert advice.",
+ posts: posts,
+ variants: "grid-text",
+ orientation: "column",
+ },
+};
diff --git a/src/components/Input/Input.js b/src/components/Input/Input.js
new file mode 100644
index 0000000..026d20e
--- /dev/null
+++ b/src/components/Input/Input.js
@@ -0,0 +1,151 @@
+import { ExclamationCircleIcon } from "@heroicons/react/20/solid";
+import PropTypes from "prop-types";
+import React from "react";
+
+/**
+ * @typedef {Object} InputFieldProps
+ * @property {string} [label]
+ * @property {string} [helperText]
+ * @property {string} [placeholder]
+ * @property {string} [type]
+ * @property {string} [value]
+ * @property {boolean} [isError]
+ * @property {boolean} [disabled]
+ * @property {string} [labelHint]
+ * @property {React.ReactNode} [leadingIcon]
+ * @property {React.ReactNode} [trailingIcon]
+ * @property {(e: React.ChangeEvent) => void} [onChange]
+ */
+
+const inputClass = {
+ error: "text-red-900 ring-red-300 focus:ring-red-500",
+ default: "text-gray-900 ring-gray-300 focus:ring-primary-600",
+};
+
+const helperTextClass = {
+ error: "text-red-600",
+ default: "text-gray-500",
+};
+
+/**
+ * @param {InputFieldProps} props
+ */
+
+export default function InputField({
+ label,
+ helperText,
+ isError,
+ disabled,
+ labelHint,
+ leadingIcon,
+ trailingIcon,
+ placeholder,
+ value,
+ type,
+ onChange,
+}) {
+ return (
+
+
+ {label && (
+
+ )}
+ {labelHint && (
+
+ {labelHint}
+
+ )}
+
+
+
+ {leadingIcon && (
+
+ {leadingIcon}
+
+ )}
+
+
+ {isError && (
+
+ )}
+ {!isError && trailingIcon && (
+
+ {trailingIcon}
+
+ )}
+
+
+ {helperText && (
+
+ {helperText}
+
+ )}
+
+ );
+}
+
+InputField.propTypes = {
+ /** The text label displayed above the input field */
+ label: PropTypes.string,
+
+ /** A short helper text displayed below the input field for additional information */
+ helperText: PropTypes.string,
+
+ /** Placeholder text displayed inside the input field when it's empty */
+ placeholder: PropTypes.string,
+
+ /** The type of input field, e.g., 'text', 'password', etc. */
+ type: PropTypes.string,
+
+ /** The current value of the input field */
+ value: PropTypes.string,
+
+ /** If true, displays the input field in an error state */
+ isError: PropTypes.bool,
+
+ /** If true, disables the input field, making it non-interactive */
+ disabled: PropTypes.bool,
+
+ /** Additional hint text displayed next to the label */
+ labelHint: PropTypes.string,
+
+ /** An optional icon component displayed at the beginning of the input field */
+ leadingIcon: PropTypes.element,
+
+ /** An optional icon component displayed at the end of the input field */
+ trailingIcon: PropTypes.element,
+
+ /** Callback function triggered when the input field's value changes */
+ onChange: PropTypes.func,
+};
diff --git a/src/components/Input/Input.stories.js b/src/components/Input/Input.stories.js
new file mode 100644
index 0000000..d72e297
--- /dev/null
+++ b/src/components/Input/Input.stories.js
@@ -0,0 +1,125 @@
+import {
+ EnvelopeIcon,
+ QuestionMarkCircleIcon,
+} from "@heroicons/react/20/solid";
+import InputField from "./Input";
+import { fn } from "@storybook/test";
+
+export default {
+ title: "Atom/Input",
+ component: InputField,
+ argTypes: {
+ label: { control: "text" },
+ helperText: { control: "text" },
+ placeholder: { control: "text" },
+ type: { control: "text" },
+ value: { control: "text" },
+ labelHint: { control: "text" },
+ isError: { control: "boolean" },
+ disabled: { control: "boolean" },
+ leadingIcon: {
+ control: "object",
+ },
+ trailingIcon: {
+ control: "object",
+ },
+ },
+ args: { onChange: fn() },
+ tags: ["autodocs"],
+};
+
+export const Primary = {
+ args: {
+ label: "Email",
+ helperText: "We'll only use this for spam.",
+ placeholder: "say something",
+ isError: false,
+ disabled: false,
+ labelHint: "Optional",
+ type: "email",
+ onChange: fn(),
+ leadingIcon: (
+
+ ),
+ trailingIcon: (
+
+ ),
+ },
+};
+
+export const Input_with_label = {
+ args: {
+ label: "Email",
+ placeholder: "say something",
+ },
+};
+
+export const Input_with_help_text = {
+ args: {
+ label: "Email",
+ placeholder: "say something",
+ helperText: "We'll only use this for spam.",
+ },
+};
+
+export const Input_with_validation_error = {
+ args: {
+ label: "Email",
+ placeholder: "say something",
+ helperText: "Not a valid email address.",
+ value: "adityacom",
+ isError: true,
+ },
+};
+
+export const Input_with_disabled_state = {
+ args: {
+ label: "Email",
+ placeholder: "say something",
+ value: "you@example.com",
+ disabled: true,
+ },
+};
+
+export const Input_without_label = {
+ args: {
+ value: "you@example.com",
+ },
+};
+
+export const Input_with_label_hint = {
+ args: {
+ label: "Email",
+ placeholder: "say something",
+ value: "you@example.com",
+ labelHint: "Optional",
+ },
+};
+
+export const Input_with_leading_icon = {
+ args: {
+ label: "Email",
+ placeholder: "say something",
+ value: "you@example.com",
+ leadingIcon: (
+
+ ),
+ },
+};
+
+export const Input_with_trailing_icon = {
+ args: {
+ label: "Email",
+ placeholder: "say something",
+ value: "you@example.com",
+ trailingIcon: (
+
+ ),
+ },
+};
diff --git a/src/stories/Configure.mdx b/src/stories/Configure.mdx
index 6a53730..9352e60 100644
--- a/src/stories/Configure.mdx
+++ b/src/stories/Configure.mdx
@@ -15,21 +15,23 @@ import Accessibility from "./assets/accessibility.png";
import Theming from "./assets/theming.png";
import AddonLibrary from "./assets/addon-library.png";
-export const RightArrow = () =>
+);
@@ -38,6 +40,7 @@ export const RightArrow = () =>
@@ -84,6 +87,7 @@ export const RightArrow = () =>
@@ -203,6 +207,7 @@ export const RightArrow = () => Discover tutorials
+