diff --git a/package.json b/package.json index f02c440..4610218 100644 --- a/package.json +++ b/package.json @@ -74,17 +74,20 @@ "@bedrock-layout/primitives": "^3.0.23", "@bedrock-layout/spacing-constants": "^3.2.5", "classnames": "^2.3.2", - "markdown-to-jsx": "^7.3.2" + "markdown-to-jsx": "^7.3.2", + "react-browser-ui": "^1.3.5", + "react-loader-spinner": "^6.1.6" }, "devDependencies": { "@auto-it/npm": "^11.0.1", "@auto-it/released": "^11.0.1", "@kickstartds/base": "^4.0.2", "@kickstartds/blog": "^4.0.2", + "@kickstartds/cambria": "^1.0.5", "@kickstartds/content": "^4.0.0", "@kickstartds/core": "^4.0.2", "@kickstartds/form": "^4.0.2", - "@kickstartds/jsonschema-utils": "^3.0.13", + "@kickstartds/jsonschema-utils": "^3.4.1", "@kickstartds/jsonschema2types": "^1.1.37", "@kickstartds/storybook-addon-component-tokens": "^3.0.0", "@kickstartds/storybook-addon-html": "^1.0.0", @@ -114,9 +117,11 @@ "happy-dom": "^13.3.1", "husky": "^8.0.3", "jest-image-snapshot": "^6.4.0", + "json-schema-traverse": "^1.0.0", "kickstartds": "^3.0.0", "lazysizes": "^5.3.2", "npm-run-all": "^4.1.5", + "object-traversal": "^1.0.1", "patch-package": "^8.0.0", "playroom": "^0.34.1", "postcss": "^8.4.29", diff --git a/src/components/testimonial/TestimonialComponent.tsx b/src/components/testimonial/TestimonialComponent.tsx index 2f6f35e..e568df2 100644 --- a/src/components/testimonial/TestimonialComponent.tsx +++ b/src/components/testimonial/TestimonialComponent.tsx @@ -16,7 +16,7 @@ export const TestimonialContextDefault = forwardRef< text={quote} source={name} byline={title} - image={image.src} + image={image && image.src} renderSource={() => ( <> {rating ? ( diff --git a/src/pages/Prompter.stories.tsx b/src/pages/Prompter.stories.tsx new file mode 100644 index 0000000..f880e9d --- /dev/null +++ b/src/pages/Prompter.stories.tsx @@ -0,0 +1,970 @@ +import { + FC, + Fragment, + PropsWithChildren, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { traverse as objectTraverse } from "object-traversal"; +import { defaultObjectForSchema } from "@kickstartds/cambria"; +import { unpack, getArgsShared } from "@kickstartds/core/lib/storybook"; +import { SelectField } from "@kickstartds/form/lib/select-field"; +import { JSONSchema } from "json-schema-typed/draft-07"; +import { JSONSchema7 } from "json-schema"; +import { InfinitySpin } from "react-loader-spinner"; +import Browser, { Chrome } from "react-browser-ui"; +import schemaTraverse from "json-schema-traverse"; +import merge from "deepmerge"; + +import { Section } from "../components/section/SectionComponent"; +import { Html } from "../components/html/HtmlComponent"; +import { Button } from "../components/button/ButtonComponent"; +import { Faq } from "../components/faq/FaqComponent"; + +import { PageProps } from "../components/cms"; +import { Contact } from "../components/contact/ContactComponent"; +import { Cta } from "../components/cta/CtaComponent"; +import { Features } from "../components/features/FeaturesComponent"; +import { Stats } from "../components/stats/StatsComponent"; +import { TeaserCard } from "../components/teaser-card/TeaserCardComponent"; +import { Testimonials } from "../components/testimonials/TestimonialsComponent"; +import { Text } from "../components/text/TextComponent"; +import { Hero } from "../components/hero/HeroComponent"; +import { Header } from "../components/header/HeaderComponent"; +import { Footer } from "../components/footer/FooterComponent"; + +import * as themes from "../themes"; + +import pageSchema from "../components/cms/page.schema.dereffed.json"; +import headerSchema from "../components/header/header.schema.dereffed.json"; +import footerSchema from "../components/footer/footer.schema.dereffed.json"; +import { VideoCurtain } from "../components/video-curtain/VideoCurtainComponent"; + +const { Tab, Divider, AddButton } = Chrome; + +const { args: headerArgs } = getArgsShared(headerSchema as JSONSchema7); +const headerProps = { + ...unpack(headerArgs), + logo: themes.dsa.logo, +}; +const { args: footerArgs } = getArgsShared(footerSchema as JSONSchema7); +const footerProps = { + ...unpack(footerArgs), + logo: themes.dsa.logo, +}; + +// import { Gallery } from "../components/gallery/GalleryComponent"; +// import { ImageStory } from "../components/image-story/ImageStoryComponent"; +// import { ImageText } from "../components/image-text/ImageTextComponent"; +// import { Logos } from "../components/logos/LogosComponent"; +// import { Mosaic } from "../components/mosaic/MosaicComponent"; +// import { Slider } from "../components/slider/SliderComponent"; +// import { VideoCurtain } from "../components/video-curtain/VideoCurtainComponent"; + +const systemPrompt = + "You are a Design System marketing specialist. You excel in creating engaging and informative content for your audience. You are using a page schema to generate content for your website. You answer with a page containing at least 4 sections, which themselves should have an average of 2 components (1-3) added to them."; + +const demoResponse = ` +{ + "type__page": "page", + "section": [ + { + "type__section": "section", + "style": "horizontalGradient", + "headline": { + "text": "Consistency Across Platforms", + "large": true, + "sub": "Ensure uniform design across all your products" + }, + "components": [ + { + "type__text": "text", + "text": "A design system provides a single source of truth for your brand’s design language. By utilizing consistent styles, components, and patterns, you can guarantee that your product delivers a cohesive experience across different devices and platforms.", + "layout": "singleColumn" + }, + { + "type__cta": "cta", + "headline": "Want to learn more about maintaining consistency?", + "sub": "Reach out to explore how a design system can unify your brand vision.", + "text": "Consistency breeds familiarity with users, enhancing user experience and satisfaction.", + "buttons": [{ "label": "Contact Us" }] + } + ], + "buttons": [] + }, + { + "type__section": "section", + "style": "accentTransition", + "headline": { + "text": "Efficiency Improvements", + "large": true, + "sub": "Streamline workflows with reusable components" + }, + "components": [ + { + "type__text": "text", + "text": "A well-defined design system allows teams to work faster by reusing components rather than creating them from scratch for each project. This not only speeds up development but also reduces errors and ensures high-quality standards are met consistently.", + "layout": "singleColumn" + }, + { + "type__features": "features", + "layout": "largeTiles", + "style": "centered", + "ctas": { "toggle": true, "style": "button" }, + "feature": [ + { + "type__feature": "feature", + "title": "Save Time", + "text": "Cut design time by up to 50% through reusability.", + "style": "centered", + "cta": { "label": "Learn More", "toggle": true, "style": "link" } + }, + { + "type__feature": "feature", + "title": "Reduce Toil", + "text": "Focus on innovation instead of repetitive design tasks.", + "style": "centered", + "cta": { + "label": "Explore Case Studies", + "toggle": true, + "style": "link" + } + } + ] + } + ], + "buttons": [ + { + "type__button": "button", + "label": "Read More", + "variant": "secondary", + "size": "medium", + "disabled": false + } + ] + }, + { + "type__section": "section", + "style": "boldTransition", + "headline": { + "text": "Facilitating Collaboration", + "large": false, + "sub": "Enhance team communication with a shared language" + }, + "components": [ + { + "type__faq": "faq", + "questions": [ + { + "question": "How does a design system support cross-functional teams?", + "answer": "By offering a shared language that aligns design and development teams, fostering smoother handoffs and reducing miscommunications." + }, + { + "question": "What tools can streamline this process?", + "answer": "Tools like Figma and Storybook integrate well with design systems to ease this collaborative effort." + } + ] + }, + { + "type__text": "text", + "text": "Adopting a design system invites seamless collaboration between designers, developers, and product managers. By establishing a common design language, it breaks down silos and promotes efficiency.", + "layout": "multiColumn" + } + ], + "buttons": [ + { + "type__button": "button", + "label": "Join Our Webinar", + "variant": "primary", + "size": "large", + "disabled": false + } + ] + }, + { + "type__section": "section", + "style": "symmetricGlow", + "headline": { + "text": "Scalability", + "large": false, + "sub": "Support growth without losing quality or consistency" + }, + "components": [ + { + "type__stats": "stats", + "stat": [ + { + "type__stat": "stat", + "number": 70, + "description": "of companies see improved scalability with design systems", + "title": "Scalable Design" + }, + { + "type__stat": "stat", + "number": 85, + "description": "agree that scalability is crucial for long-term success", + "title": "Long-term Benefits" + } + ] + }, + { + "type__cta": "cta", + "headline": "Scale with Confidence", + "sub": "Design systems allow you to maintain brand integrity, whether you're creating a single landing page or launching a multisite platform.", + "text": "Talk to our experts about scaling your business efficiently without compromising on quality.", + "buttons": [{ "label": "Contact Sales" }] + } + ], + "buttons": [] + }, + { + "type__section": "section", + "style": "symmetricGlow", + "headline": { + "text": "Accessibility and Inclusivity", + "large": false, + "sub": "Build products that everyone can use" + }, + "components": [ + { + "type__features": "features", + "layout": "list", + "style": "stack", + "ctas": { "toggle": true, "style": "link" }, + "feature": [ + { + "type__feature": "feature", + "title": "Predefined Accessibility", + "text": "Design systems come equipped with accessible components, which helps further inclusivity in design.", + "style": "intext", + "cta": { + "label": "More About Inclusive Design", + "toggle": true, + "style": "link" + } + }, + { + "type__feature": "feature", + "title": "Regulatory Compliance", + "text": "Have confidence that your product meets necessary accessibility standards and regulations.", + "style": "stack", + "cta": { + "label": "Start a Conversation", + "toggle": true, + "style": "link" + } + } + ] + } + ], + "buttons": [ + { + "type__button": "button", + "label": "Get Started", + "variant": "primary", + "size": "medium", + "disabled": false + } + ] + } + ], + "seo": { + "type__seo": "seo", + "title": "5 Key Reasons to Adopt a Design System", + "description": "Explore the essential benefits of using a design system to enhance consistency, efficiency, collaboration, scalability, and accessibility.", + "keywords": "Design System, Consistency, Efficiency, Collaboration, Scalability, Accessibility" + } +} +`; + +const unsupportedKeywords = [ + "format", + "minItems", + "maxItems", + "minimum", + "maximum", + "examples", + "default", + "$id", + "$schema", +]; + +const propertiesToDrop = [ + "backgroundColor", + "spotlight", + "headerSpacing", + "switchOrder", + "align", + "textAlign", + "colorNeutral", + "target", + "contentAlign", + "textColor", + "highlightText", + "fullWidth", + "padding", + "mobileImageLast", + "desktopImageLast", + "overlay", + "indent", + "imageRatio", + "image", + "width", + "inverted", + "spaceBefore", + "spaceAfter", + "height", +]; + +const components = [ + "page", + "section", + "contact", + "cta", + "faq", + "features", + // "gallery", + "stats", + "teaser-card", + "testimonials", + "text", + // "image-story", + // "image-text", + // "logos", + "hero", + // "mosaic", + // "slider", + "seo", + // "video-curtain", +]; + +const componentMap = { + contact: Contact, + cta: Cta, + faq: Faq, + features: Features, + // gallery: Gallery, + stats: Stats, + "teaser-card": TeaserCard, + testimonials: Testimonials, + text: Text, + // "image-story": ImageStory, + // "image-text": ImageText, + // logos: Logos, + hero: Hero, + // mosaic: Mosaic, + // slider: Slider, + // "video-curtain": VideoCurtain, +}; + +const schemaMap = { + page: {}, + section: {}, + contact: {}, + cta: {}, + faq: {}, + features: {}, + // gallery: {}, + stats: {}, + "teaser-card": {}, + testimonials: {}, + text: {}, + // "image-story": {}, + // "image-text": {}, + // logos: {}, + hero: {}, + // mosaic: {}, + // slider: {}, + seo: {}, + // "video-curtain": {}, +}; + +// TODO type this, might involve pre-processing the schema to generate types in filesystem using `json-schema-to-typescript` +const processResponse = (response: Record): PageProps => { + objectTraverse( + response, + ({ value }) => { + if (typeof value === "object" && !Array.isArray(value)) { + const typePropKey = Object.keys(value).find((key) => + key.startsWith("type__") + ); + + if (typePropKey) { + const type = typePropKey.split("type__")[1]; + const schema = schemaMap[type]; + + if (schema) { + const defaults = defaultObjectForSchema( + schema as JSONSchema.Object + ); + + delete value[typePropKey]; + value.type = type; + + for (const prop of Object.keys(defaults)) { + value[prop] = value[prop] + ? typeof value[prop] === "object" && !Array.isArray(value[prop]) + ? merge(value[prop], defaults[prop]) + : value[prop] + : defaults[prop]; + } + } + } + } + }, + { traversalType: "breadth-first" } + ); + + return response as PageProps; +}; + +/* + +Compare flatten to this one from Starter `prepareProject.js`: + + traverse(preset.preset, ({ parent, key, value }) => { + if (typeof value === "object" && isNaN(key) && !Array.isArray(value)) { + for (const [propKey, propValue] of Object.entries(value)) { + parent[`${key}_${propKey}`] = propValue; + } + delete parent[key]; + } + }); + +*/ + +const processPage = (page: PageProps): Record => { + objectTraverse( + page, + ({ value }) => { + if (typeof value === "object" && value.type) { + value.component = value.type; + + for (const prop of Object.keys(value)) { + if ( + !value[prop].type && + typeof value[prop] === "object" && + !Array.isArray(value[prop]) + ) { + for (const nestedProp of Object.keys(value[prop])) { + value[`${prop}_${nestedProp}`] = structuredClone( + value[prop][nestedProp] + ); + } + delete value[prop]; + } + } + } + }, + { traversalType: "breadth-first" } + ); + + return page as Record; +}; + +// TODO handle `type` in props, currently just gets passed through +const Page: FC> = ({ section }) => { + return ( + <> +
+ {section.map((section, index) => { + const { components, ...props } = section; + return ( +
+ {components.map((component, index) => { + const Component = componentMap[component.type]; + return ; + })} +
+ ); + })} +