diff --git a/.changeset/hot-bananas-flash.md b/.changeset/hot-bananas-flash.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/hot-bananas-flash.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/components/package.json b/packages/components/package.json index 7a02a7aaf51..f5f1f6c0144 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -68,6 +68,7 @@ "@react-types/shared": "^3.19.0", "classnames": "^2.3.2", "date-fns": "^2.30.0", + "react-animate-height": "^3.2.2", "react-focus-on": "^3.9.1", "react-intl": "^6.4.4", "react-popper": "^2.3.0", diff --git a/packages/components/src/Brand/Brand.module.scss b/packages/components/src/Brand/Brand.module.scss new file mode 100644 index 00000000000..457b6bb32f4 --- /dev/null +++ b/packages/components/src/Brand/Brand.module.scss @@ -0,0 +1,4 @@ +.img { + max-inline-size: 100%; + display: flex; +} diff --git a/packages/components/src/Brand/Brand.tsx b/packages/components/src/Brand/Brand.tsx new file mode 100644 index 00000000000..73ef18d9f54 --- /dev/null +++ b/packages/components/src/Brand/Brand.tsx @@ -0,0 +1,44 @@ +import React, { HTMLAttributes } from "react" +import { assetUrl } from "@kaizen/hosted-assets" +import { OverrideClassName } from "~types/OverrideClassName" +import styles from "./Brand.module.scss" + +export type BrandProps = { + variant: + | "logo-horizontal" + | "logo-vertical" + | "enso" + | "collective-intelligence" + alt: string + reversed?: boolean +} & OverrideClassName> + +export const Brand = ({ + variant, + alt, + reversed = false, + classNameOverride, + ...restProps +}: BrandProps): JSX.Element => { + const brandTheme = reversed ? "-reversed" : "-default" + + return ( + + + + {alt} + + ) +} + +Brand.displayName = "Brand" diff --git a/packages/components/src/Brand/_docs/Brand.mdx b/packages/components/src/Brand/_docs/Brand.mdx new file mode 100644 index 00000000000..79c9263cea4 --- /dev/null +++ b/packages/components/src/Brand/_docs/Brand.mdx @@ -0,0 +1,33 @@ +import { Canvas, Controls, DocsStory, Meta } from "@storybook/blocks" +import { ResourceLinks, KaioNotification, Installation } from "~storybook/components" +import * as BrandStories from "./Brand.stories" + + + +# Brand + + + + + + + +## Overview + +> “Brand is a promise to your customer, culture is how you deliver on it.” +> — Didier Elzinga + + + + +## API + + diff --git a/packages/components/src/Brand/_docs/Brand.stickersheet.stories.tsx b/packages/components/src/Brand/_docs/Brand.stickersheet.stories.tsx new file mode 100644 index 00000000000..8628f2c2efc --- /dev/null +++ b/packages/components/src/Brand/_docs/Brand.stickersheet.stories.tsx @@ -0,0 +1,62 @@ +import React from "react" +import { Meta } from "@storybook/react" +import { + StickerSheet, + StickerSheetStory, +} from "~storybook/components/StickerSheet" +import { Brand } from "../index" + +export default { + title: "KAIO-staging/Brand", + parameters: { + chromatic: { disable: false }, + controls: { disable: true }, + }, +} satisfies Meta + +const StickerSheetTemplate: StickerSheetStory = { + render: ({ isReversed }) => ( + + + + + + + + + + + + +
+ +
+
+
+
+ ), +} + +export const StickerSheetDefault: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (Default)", +} + +export const StickerSheetReversed: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (Reversed)", + parameters: { backgrounds: { default: "Purple 700" } }, + args: { isReversed: true }, +} diff --git a/packages/components/src/Brand/_docs/Brand.stories.tsx b/packages/components/src/Brand/_docs/Brand.stories.tsx new file mode 100644 index 00000000000..2c2816c7602 --- /dev/null +++ b/packages/components/src/Brand/_docs/Brand.stories.tsx @@ -0,0 +1,31 @@ +import { Meta, StoryObj } from "@storybook/react" +import { StickerSheetStory } from "~storybook/components/StickerSheet" +import { Brand } from "../index" +import { StickerSheetDefault } from "./Brand.stickersheet.stories" + +const meta = { + title: "KAIO-staging/Brand", + component: Brand, + args: { + variant: "logo-horizontal", + alt: "Logo Horizontal", + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Playground: Story = { + parameters: { + docs: { + canvas: { + sourceState: "shown", + }, + }, + }, +} + +export const Variants: StickerSheetStory = { + ...StickerSheetDefault, +} diff --git a/packages/components/src/Brand/index.ts b/packages/components/src/Brand/index.ts new file mode 100644 index 00000000000..949035e9488 --- /dev/null +++ b/packages/components/src/Brand/index.ts @@ -0,0 +1 @@ +export * from "./Brand" diff --git a/packages/components/src/Collapsible/Collapsible/Collapsible.module.scss b/packages/components/src/Collapsible/Collapsible/Collapsible.module.scss new file mode 100644 index 00000000000..8a5823f08b5 --- /dev/null +++ b/packages/components/src/Collapsible/Collapsible/Collapsible.module.scss @@ -0,0 +1,115 @@ +@import "~@kaizen/design-tokens/sass/border"; +@import "~@kaizen/design-tokens/sass/shadow"; +@import "~@kaizen/design-tokens/sass/color"; +@import "~@kaizen/design-tokens/sass/spacing"; +@import "~@kaizen/design-tokens/sass/animation"; + +$heading-active-color: $color-gray-200; +$border-color: rgba($color-gray-600-rgb, 0.2); + +.header { + display: flex; + align-items: center; + text-align: left; + background-color: $color-white; + border: none; + transition: background-color $animation-duration-rapid; + padding: $spacing-sm $spacing-sm $spacing-sm $spacing-md; + + &:hover { + background-color: $heading-active-color; + } + + [dir="rtl"] & { + text-align: right; + padding-left: $spacing-sm; + padding-right: $spacing-md; + } + + // Round the bottom corners of the header so when the container is open, the + // header background is not rounded on the corners and flush with the content beneath. + .open { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } +} + +// We need a full border radius on this container element, then have classes +// beneath to toggle nested borders off and on for different use cases. +.container { + background-color: $color-white; + box-shadow: $shadow-small-box-shadow; + border-radius: $border-borderless-border-radius; +} + +.single { + .header { + border-radius: $border-borderless-border-radius; + } +} + +.separated { + & + .separated { + margin-top: calc(#{$spacing-md} * 0.3); + margin-right: 0; + margin-left: 0; + } + + .header { + border-radius: $border-borderless-border-radius; + } +} + +// When a collapsible group is rendered, we need the first group to have a rounded +// on top and the last group to have a rounded bottom edge. Then when the last group +// is open we remove the rounded edge as the content sits beneath and needs to be straight. +.groupItem { + & + .groupItem { + border-top: 1px $border-solid-border-style $border-color; + } + + &:first-of-type > .header { + border-top-left-radius: $border-borderless-border-radius; + border-top-right-radius: $border-borderless-border-radius; + } + + &:last-of-type > .header:not(.open) { + border-bottom-left-radius: $border-borderless-border-radius; + border-bottom-right-radius: $border-borderless-border-radius; + } +} + +.chevronButton:hover { + // hack to get rid of the IconButton hover styling because it clashes with the hover styling on the Collapsible header + background-color: transparent !important; /* stylelint-disable-line declaration-no-important */ +} + +.title { + flex: 1 1 auto; +} + +.defaultVariant { + background-color: $heading-active-color; +} + +.clearVariant { + border-bottom: 1px $border-solid-border-style $border-color; +} + +.sticky { + position: -webkit-sticky; /* stylelint-disable-line value-no-vendor-prefix */ + position: sticky; + z-index: 10; +} + +.section { + padding: $spacing-md; +} + +.noPadding { + padding: 0; +} + +.hide { + display: none; +} diff --git a/packages/components/src/Collapsible/Collapsible/Collapsible.spec.tsx b/packages/components/src/Collapsible/Collapsible/Collapsible.spec.tsx new file mode 100644 index 00000000000..46854381a1d --- /dev/null +++ b/packages/components/src/Collapsible/Collapsible/Collapsible.spec.tsx @@ -0,0 +1,115 @@ +import React from "react" +import { queryByTestId, render, waitFor } from "@testing-library/react" +import userEvent from "@testing-library/user-event" +import { Collapsible } from "./Collapsible" + +const user = userEvent.setup() + +describe("", () => { + it("includes the 'sticky' class on buttons when the 'sticky' prop is specified", () => { + const { getByTestId } = render( + + First panel content + + ) + + const collapsibleContainer = getByTestId("collapsible-header-1") + + expect(collapsibleContainer.classList.contains("sticky")).toBeTruthy() + }) + + it("toggles the height of the section on click of the button", async () => { + const { getByTestId } = render( + + First panel content + + ) + + const collapsible = getByTestId("collapsible-header-1") + const section = getByTestId("collapsible-section-1") + + await user.click(collapsible) + await waitFor(() => { + expect(section.style.height).toEqual("0px") + }) + + await user.click(collapsible) + await waitFor(() => { + expect(section.style.height).toEqual("auto") + }) + }) + + it("gives precedence to renderHeader over title", () => { + const { container, getByTestId } = render( + ( +
This title should be rendered
+ )} + > + First panel content +
+ ) + + const titleText = getByTestId("collapsible-header-1").querySelector("div") + + expect(titleText).toHaveTextContent("This title should be rendered") + expect( + queryByTestId(container as HTMLElement, "collapsible-button-title-1") + ).toBeNull() + }) + + it("doesn't render section content when lazyLoad is enabled", () => { + const { container } = render( + +
First panel content
+
+ ) + + expect( + queryByTestId(container as HTMLElement, "lazy-load-content") + ).toBeNull() + }) + + it("runs the onToggle callback", async () => { + const onToggle = jest.fn() + + const { getByTestId } = render( + + First panel content + + ) + + const collapsible = getByTestId("collapsible-header-1") + + await user.click(collapsible) + await waitFor(() => { + expect(onToggle).toHaveBeenCalledTimes(1) + expect(onToggle).toHaveBeenCalledWith(false, "1") + }) + + await user.click(collapsible) + await waitFor(() => { + expect(onToggle).toHaveBeenCalledTimes(2) + expect(onToggle).toHaveBeenCalledWith(true, "1") + }) + }) + + it("respects controlled mode (stays open after click)", async () => { + const { getByTestId } = render( + + First panel content + + ) + + const collapsible = getByTestId("collapsible-header-1") + const section = getByTestId("collapsible-section-1") + + await user.click(collapsible) + await waitFor(() => { + expect(section.style.height).toEqual("auto") + }) + }) +}) diff --git a/packages/components/src/Collapsible/Collapsible/Collapsible.tsx b/packages/components/src/Collapsible/Collapsible/Collapsible.tsx new file mode 100644 index 00000000000..de803c239ac --- /dev/null +++ b/packages/components/src/Collapsible/Collapsible/Collapsible.tsx @@ -0,0 +1,185 @@ +import React, { HTMLAttributes } from "react" +import classnames from "classnames" +import AnimateHeight from "react-animate-height" +import { v4 } from "uuid" +import { Heading } from "@kaizen/typography" +import { IconButton } from "~components/Button" +import { ChevronUpIcon, ChevronDownIcon } from "~components/Icons" +import { OverrideClassName } from "~types/OverrideClassName" +import { Sticky } from "../types" +import styles from "./Collapsible.module.scss" + +type Variant = "default" | "clear" + +export type CollapsibleProps = { + children: JSX.Element | JSX.Element[] | string + title: string + renderHeader?: (title: string) => JSX.Element | JSX.Element[] + open?: boolean + group?: boolean + separated?: boolean + sticky?: Sticky + noSectionPadding?: boolean + onToggle?: (open: boolean, id: string) => void + /** + * By default, the header will change background colour when open. When the variant + * is set to 'clear', it will not have a background but a border-bottom will appear + * to separate the heading from the content. + */ + variant?: Variant + /** + * Will avoid rendering the content until required (especially important when you + * have queries inside sections). + * Removes animation. + */ + lazyLoad?: boolean + /** + * Disables internal `open` state, allowing it to be controlled in the usage. + */ + controlled?: boolean +} & OverrideClassName> + +type State = { + open: boolean + id: string +} + +/** + * {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3082094383/Collapsible Guidance} | + * {@link https://cultureamp.design/?path=/docs/components-collapsible--docs Storybook} + */ +export class Collapsible extends React.Component { + public state = { + open: !!this.props.open, + id: this.props.id || v4(), + } + + public render(): JSX.Element { + const { + id: _, + children, + title, + renderHeader, + open: _propsOpen, // Unused, but extracted so as not to spread into the container + group, + separated, + sticky, + noSectionPadding, + onToggle: _onToggle, // Unused, but extracted so as not to spread into the container + variant = "default", + lazyLoad, + controlled: _controlled, // Unused, but extracted so as not to spread into the container + classNameOverride, + ...props + } = this.props + + const { id } = this.state + const buttonId = `${id}-button` + const sectionId = `${id}-section` + const open = this.getOpen() + const isContainer = !group || separated + + return ( +
+ {/* Disabling these a11y linting errors because there is an IconButton that mitigates these concerns. The onClick here is just an additional layer. */} + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, + jsx-a11y/no-static-element-interactions */} +
+ {renderHeader !== undefined ? ( + renderHeader(title) + ) : ( +
+ + {title} + +
+ )} +
+ + ) : ( + + ) + } + type="button" + aria-expanded={open} + aria-controls={sectionId} + data-testid={`collapsible-button-${id}`} + id={buttonId} + onClick={this.handleButtonPress} + classNameOverride={styles.chevronButton} + /> +
+
+ {(!lazyLoad || open) && ( + +
+ {children} +
+
+ )} +
+ ) + } + + private getOpen = (): boolean | undefined => + this.props.controlled ? this.props.open : this.state.open + + private handleSectionToggle = (): void => { + const { onToggle, controlled } = this.props + const open = this.getOpen() + + onToggle && onToggle(!open, this.state.id) + + if (!controlled) { + this.setState({ + open: !open, + }) + } + } + + private handleButtonPress = (event: React.MouseEvent): void => { + event.stopPropagation() + this.handleSectionToggle() + } +} diff --git a/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.mdx b/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.mdx new file mode 100644 index 00000000000..729b55c0299 --- /dev/null +++ b/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.mdx @@ -0,0 +1,38 @@ +import { Canvas, Controls, DocsStory, Meta } from "@storybook/blocks" +import { ResourceLinks, KaioNotification, Installation } from "~storybook/components" +import * as CollapsibleStories from "./Collapsible.stories" + + + +# Collapsible + + + + + + + +## Overview + +Collapsibles are used to collapse and expand content inline on a page. This lets users progressively disclose information as desired. + + + + +## API + + + + + + + + diff --git a/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.stickersheet.stories.tsx b/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.stickersheet.stories.tsx new file mode 100644 index 00000000000..8b8928ecc68 --- /dev/null +++ b/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.stickersheet.stories.tsx @@ -0,0 +1,72 @@ +import React from "react" +import { Meta } from "@storybook/react" +import { Heading } from "@kaizen/typography" +import { + StickerSheet, + StickerSheetStory, +} from "~storybook/components/StickerSheet" +import { Collapsible, CollapsibleProps } from "../index" + +export default { + title: "KAIO-staging/Collapsibles/Collapsible", + parameters: { + chromatic: { disable: false }, + controls: { disable: true }, + }, +} satisfies Meta + +const CollapsibleWrapped = ( + args: Omit +): JSX.Element => ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac + scelerisque sem, vel ultricies justo. Donec eu porttitor ante, nec gravida + orci. Nulla facilisi. Cras varius erat id fermentum mattis. Mauris bibendum + vestibulum erat, quis blandit metus viverra sit amet. Vivamus pretium vitae + turpis ut condimentum. Sed vulputate magna nisl, in cursus urna hendrerit + et. Aenean semper, est non feugiat sodales, nisl ligula aliquet lorem, sit + amet scelerisque arcu quam a sapien. Donec in viverra urna. + +) + +const StickerSheetTemplate: StickerSheetStory = { + render: () => ( + + + + + + + + + + + + + + + + ( + + {title} + + )} + /> + + + + ), +} + +export const StickerSheetDefault: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (Default)", +} + +export const StickerSheetRTL: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (RTL)", + parameters: { textDirection: "rtl" }, +} diff --git a/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.stories.tsx b/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.stories.tsx new file mode 100644 index 00000000000..04cfc2bd74a --- /dev/null +++ b/packages/components/src/Collapsible/Collapsible/_docs/Collapsible.stories.tsx @@ -0,0 +1,99 @@ +import React from "react" +import { Meta, StoryObj } from "@storybook/react" +import { Heading, Paragraph } from "@kaizen/typography" +import { AddIcon } from "~components/Icons" +import { Collapsible } from "../index" + +const meta = { + title: "KAIO-staging/Collapsibles/Collapsible", + component: Collapsible, + args: { + children: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus + ac scelerisque sem, vel ultricies justo. Donec eu porttitor ante, + nec gravida orci. Nulla facilisi. Cras varius erat id fermentum + mattis. Mauris bibendum vestibulum erat, quis blandit metus viverra + sit amet. Vivamus pretium vitae turpis ut condimentum. Sed vulputate + magna nisl, in cursus urna hendrerit et. Aenean semper, est non + feugiat sodales, nisl ligula aliquet lorem, sit amet scelerisque + arcu quam a sapien. Donec in viverra urna.`, + }, + parameters: { + backgrounds: { default: "Gray 100" }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Playground: Story = { + args: { + title: "Single Collapsible", + }, + parameters: { + backgrounds: { default: "Gray 100" }, + docs: { + canvas: { + sourceState: "shown", + }, + }, + }, +} + +export const NoPadding: Story = { + args: { + title: "No padding", + }, + render: ({ title }) => ( + + + In that case you should use the 'noSectionPadding' prop. + + + ), +} + +export const Clear: Story = { + args: { + title: "Clear", + }, + render: ({ title }) => ( + + The header becomes clear + + ), +} + +export const CustomHeader: Story = { + args: { + title: "Custom header", + }, + render: ({ title: standardTitle }) => ( + ( + + + {title} + + + )} + > + + You can create a custom header using the renderHeader prop. + + + ), +} + +export const Sticky: Story = { + args: { + title: "Sticky header", + }, + render: ({ title }) => ( + + This does not work in Storybook docs, so use this as a code example only. + + ), +} diff --git a/packages/components/src/Collapsible/Collapsible/index.ts b/packages/components/src/Collapsible/Collapsible/index.ts new file mode 100644 index 00000000000..f0b8604f08a --- /dev/null +++ b/packages/components/src/Collapsible/Collapsible/index.ts @@ -0,0 +1 @@ +export * from "./Collapsible" diff --git a/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.module.scss b/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.module.scss new file mode 100644 index 00000000000..9e1d01eb9ff --- /dev/null +++ b/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.module.scss @@ -0,0 +1,10 @@ +@import "~@kaizen/design-tokens/sass/border"; +@import "~@kaizen/design-tokens/sass/shadow"; + +// We need a full border radius on this container element, then have classes +// beneath to toggle nested borders off and on for different use cases. +.container { + background-color: white; + box-shadow: $shadow-small-box-shadow; + border-radius: $border-borderless-border-radius; +} diff --git a/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.spec.tsx b/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.spec.tsx new file mode 100644 index 00000000000..482ac6f6f7f --- /dev/null +++ b/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.spec.tsx @@ -0,0 +1,31 @@ +import React from "react" +import { render, waitFor } from "@testing-library/react" +import userEvent from "@testing-library/user-event" +import { Collapsible } from "~components/Collapsible/Collapsible" +import { CollapsibleGroup } from "./CollapsibleGroup" + +const user = userEvent.setup() + +// @todo write a test to ensure right props are passed to children? +describe("", () => { + it("only toggles the height of the clicked panel in a group", async () => { + const { getByTestId } = render( + + + First panel content + + + Second panel content + + + ) + + const header = getByTestId("collapsible-header-1") + const section = getByTestId("collapsible-section-2") + + await user.click(header) + await waitFor(() => { + expect(section.style.height).toEqual("auto") + }) + }) +}) diff --git a/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.tsx b/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.tsx new file mode 100644 index 00000000000..d8dbfe7397a --- /dev/null +++ b/packages/components/src/Collapsible/CollapsibleGroup/CollapsibleGroup.tsx @@ -0,0 +1,46 @@ +import React, { HTMLAttributes } from "react" +import classnames from "classnames" +import { CollapsibleProps } from "~components/Collapsible/Collapsible" +import { Sticky } from "~components/Collapsible/types" +import { OverrideClassName } from "~types/OverrideClassName" +import styles from "./CollapsibleGroup.module.scss" + +export type CollapsibleGroupProps = { + children: Array> + separated?: boolean + sticky?: Sticky + noSectionPadding?: boolean + lazyLoad?: boolean + onToggle?: (open: boolean, id: string) => void +} & Omit>, "children"> + +/** + * {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3082094383/Collapsible Guidance} | + * {@link https://cultureamp.design/?path=/docs/components-collapsible-collapsible-group--docs Storybook} + */ +export const CollapsibleGroup = ({ + children, + separated = false, + sticky, + noSectionPadding = false, + lazyLoad = false, + onToggle, + classNameOverride, + ...props +}: CollapsibleGroupProps): JSX.Element => ( +
+ {React.Children.map(children, collapsible => + React.cloneElement(collapsible, { + group: true, + separated, + sticky, + noSectionPadding, + lazyLoad, + onToggle, + }) + )} +
+) diff --git a/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.mdx b/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.mdx new file mode 100644 index 00000000000..b6f466f116b --- /dev/null +++ b/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.mdx @@ -0,0 +1,41 @@ +import { Canvas, Controls, Meta } from "@storybook/blocks" +import { ResourceLinks, KaioNotification, Installation } from "~storybook/components" +import * as CollapsibleGroupStories from "./CollapsibleGroup.stories" + + + +# CollapsibleGroup + + + + + + +## Overview + +Collapsibles are used to collapse and expand content inline on a page. This lets users progressively disclose information as desired. + + + + +## API + +### Re-use of Collapsible API + +The `CollapsibleGroup` exposes some of the top level `Collapsible` props so you can apply them to all Collapsibles within the Group. + + - `sticky` + - `noSectionPadding` + - `lazyLoad` + - `onToggle` + +### Separated + diff --git a/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.stickersheet.stories.tsx b/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.stickersheet.stories.tsx new file mode 100644 index 00000000000..1f1116baa17 --- /dev/null +++ b/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.stickersheet.stories.tsx @@ -0,0 +1,75 @@ +import React from "react" +import { Meta } from "@storybook/react" +import { + Collapsible, + CollapsibleProps, +} from "~components/Collapsible/Collapsible" +import { + StickerSheet, + StickerSheetStory, +} from "~storybook/components/StickerSheet" +import { CollapsibleGroup, CollapsibleGroupProps } from "../index" + +export default { + title: "KAIO-staging/Collapsibles/CollapsibleGroup", + parameters: { + chromatic: { disable: false }, + controls: { disable: true }, + }, +} satisfies Meta + +const CollapsibleWrapped = ( + args: Omit +): JSX.Element => ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac + scelerisque sem, vel ultricies justo. Donec eu porttitor ante, nec gravida + orci. Nulla facilisi. Cras varius erat id fermentum mattis. Mauris bibendum + vestibulum erat, quis blandit metus viverra sit amet. Vivamus pretium vitae + turpis ut condimentum. Sed vulputate magna nisl, in cursus urna hendrerit + et. Aenean semper, est non feugiat sodales, nisl ligula aliquet lorem, sit + amet scelerisque arcu quam a sapien. Donec in viverra urna. + +) + +const CollapsibleGroupWrapped = ( + args: Omit +): JSX.Element => ( + + + + + +) + +const StickerSheetTemplate: StickerSheetStory = { + render: ({ isReversed }) => ( + + + + + + + + + + + + + + + + + ), +} + +export const StickerSheetDefault: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (Default)", +} + +export const StickerSheetRTL: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (RTL)", + parameters: { textDirection: "rtl" }, +} diff --git a/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.stories.tsx b/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.stories.tsx new file mode 100644 index 00000000000..5e4eb2c3900 --- /dev/null +++ b/packages/components/src/Collapsible/CollapsibleGroup/_docs/CollapsibleGroup.stories.tsx @@ -0,0 +1,68 @@ +import React from "react" +import { Meta, StoryObj } from "@storybook/react" +import { + Collapsible, + CollapsibleProps, +} from "~components/Collapsible/Collapsible" +import { CollapsibleGroup, CollapsibleGroupProps } from "../index" + +const CollapsibleWrapped = ( + args: Omit +): JSX.Element => ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac + scelerisque sem, vel ultricies justo. Donec eu porttitor ante, nec gravida + orci. Nulla facilisi. Cras varius erat id fermentum mattis. Mauris bibendum + vestibulum erat, quis blandit metus viverra sit amet. Vivamus pretium vitae + turpis ut condimentum. Sed vulputate magna nisl, in cursus urna hendrerit + et. Aenean semper, est non feugiat sodales, nisl ligula aliquet lorem, sit + amet scelerisque arcu quam a sapien. Donec in viverra urna. + +) + +const CollapsibleGroupWrapped = ( + args: Omit +): JSX.Element => ( + + + + + +) + +const meta = { + title: "KAIO-staging/Collapsibles/CollapsibleGroup", + component: CollapsibleGroup, + parameters: { backgrounds: { default: "Gray 100" } }, + args: { + children: [ + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + , + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + , + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + , + ], + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Playground: Story = { + parameters: { + docs: { + canvas: { + sourceState: "shown", + }, + }, + }, +} + +export const Separated: Story = { + render: () => , +} diff --git a/packages/components/src/Collapsible/CollapsibleGroup/index.ts b/packages/components/src/Collapsible/CollapsibleGroup/index.ts new file mode 100644 index 00000000000..09aab2b2405 --- /dev/null +++ b/packages/components/src/Collapsible/CollapsibleGroup/index.ts @@ -0,0 +1 @@ +export * from "./CollapsibleGroup" diff --git a/packages/components/src/Collapsible/ExpertAdviceCollapsible/ExpertAdviceCollapsible.module.scss b/packages/components/src/Collapsible/ExpertAdviceCollapsible/ExpertAdviceCollapsible.module.scss new file mode 100644 index 00000000000..81248bd5aee --- /dev/null +++ b/packages/components/src/Collapsible/ExpertAdviceCollapsible/ExpertAdviceCollapsible.module.scss @@ -0,0 +1,50 @@ +@import "~@kaizen/design-tokens/sass/border"; +@import "~@kaizen/design-tokens/sass/color"; +@import "~@kaizen/design-tokens/sass/spacing"; + +.expertAdviceContainer { + background-color: $color-purple-100; + border: 2px $border-solid-border-style $color-purple-300; + box-shadow: none; + color: $color-purple-800; + + > div { + &:first-of-type { + background-color: $color-purple-100; + border-radius: $border-borderless-border-radius; + + &:hover { + background-color: $color-purple-100; + } + } + } +} + +.expertAdviceContainer:hover { + border-color: $color-purple-500; +} + +.expertAdviceHeader { + background-color: $color-purple-100; + flex: 1 1 auto; + align-items: center; + display: flex; +} + +.expertAdviceHeading { + color: $color-purple-600; + + .expertAdviceContainer:hover & { + color: $color-purple-700; + } +} + +.expertAdviceIcon { + margin-inline-end: $spacing-sm; + width: 1.75rem; + height: 1.75rem; +} + +.expertAdviceSection { + padding: 0 $spacing-md $spacing-md; +} diff --git a/packages/components/src/Collapsible/ExpertAdviceCollapsible/ExpertAdviceCollapsible.tsx b/packages/components/src/Collapsible/ExpertAdviceCollapsible/ExpertAdviceCollapsible.tsx new file mode 100644 index 00000000000..f9e6343b21a --- /dev/null +++ b/packages/components/src/Collapsible/ExpertAdviceCollapsible/ExpertAdviceCollapsible.tsx @@ -0,0 +1,50 @@ +import React from "react" +import { Heading } from "@kaizen/typography" +import { Brand } from "~components/Brand" +import { + CollapsibleProps, + Collapsible, +} from "~components/Collapsible/Collapsible" +import styles from "./ExpertAdviceCollapsible.module.scss" + +export type ExpertAdviceCollapsibleProps = Omit< + CollapsibleProps, + "renderHeader" | "variant" | "group" | "separated" +> + +/** + * {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3082094383/Collapsible Guidance} | + * {@link https://cultureamp.design/?path=/docs/components-collapsible--docs Storybook} + */ +export const ExpertAdviceCollapsible = ( + props: ExpertAdviceCollapsibleProps +): JSX.Element => ( + ( + <> +
+ + + {props.title} + +
+ + )} + > +
{props.children}
+
+) + +ExpertAdviceCollapsible.displayName = "ExpertAdviceCollapsible" diff --git a/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.mdx b/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.mdx new file mode 100644 index 00000000000..3f4351eabb5 --- /dev/null +++ b/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.mdx @@ -0,0 +1,28 @@ +import { Canvas, Controls, Meta } from "@storybook/blocks" +import { ResourceLinks, KaioNotification, Installation } from "~storybook/components" +import * as ExpertAdviceCollapsibleStories from "./ExpertAdviceCollapsible.stories" + + + +# ExpertAdviceCollapsible + + + + + + + +## Overview + +Collapsible full of Expert advice. + + + diff --git a/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.stickersheets.stories.tsx b/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.stickersheets.stories.tsx new file mode 100644 index 00000000000..82d32421897 --- /dev/null +++ b/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.stickersheets.stories.tsx @@ -0,0 +1,55 @@ +import React from "react" +import { Meta } from "@storybook/react" +import { + StickerSheet, + StickerSheetStory, +} from "~storybook/components/StickerSheet" +import { ExpertAdviceCollapsible, ExpertAdviceCollapsibleProps } from "../index" + +export default { + title: "KAIO-staging/Collapsibles/ExpertAdviceCollapsible", + parameters: { + chromatic: { disable: false }, + controls: { disable: true }, + }, +} satisfies Meta + +const ExpertAdviceCollapsibleWrapped = ( + args: Omit +): JSX.Element => ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac + scelerisque sem, vel ultricies justo. Donec eu porttitor ante, nec gravida + orci. Nulla facilisi. Cras varius erat id fermentum mattis. Mauris bibendum + vestibulum erat, quis blandit metus viverra sit amet. Vivamus pretium vitae + turpis ut condimentum. Sed vulputate magna nisl, in cursus urna hendrerit + et. Aenean semper, est non feugiat sodales, nisl ligula aliquet lorem, sit + amet scelerisque arcu quam a sapien. Donec in viverra urna. + +) + +const StickerSheetTemplate: StickerSheetStory = { + render: () => ( + + + + + + + + + + + ), +} + +export const StickerSheetDefault: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (Default)", +} + +export const StickerSheetRTL: StickerSheetStory = { + ...StickerSheetTemplate, + name: "Sticker Sheet (RTL)", + parameters: { textDirection: "rtl" }, +} diff --git a/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.stories.tsx b/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.stories.tsx new file mode 100644 index 00000000000..eb60077e1e5 --- /dev/null +++ b/packages/components/src/Collapsible/ExpertAdviceCollapsible/_docs/ExpertAdviceCollapsible.stories.tsx @@ -0,0 +1,32 @@ +import React from "react" +import { Meta, StoryObj } from "@storybook/react" +import { Paragraph } from "@kaizen/typography" +import { ExpertAdviceCollapsible } from "../index" + +const meta = { + title: "KAIO-staging/Collapsibles/ExpertAdviceCollapsible", + component: ExpertAdviceCollapsible, + parameters: { chromatic: { disable: false } }, + args: { + title: "Expert advice collapsible", + children: ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + + ), + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Playground: Story = { + parameters: { + docs: { + canvas: { + sourceState: "shown", + }, + }, + }, +} diff --git a/packages/components/src/Collapsible/ExpertAdviceCollapsible/index.ts b/packages/components/src/Collapsible/ExpertAdviceCollapsible/index.ts new file mode 100644 index 00000000000..6d14d37a739 --- /dev/null +++ b/packages/components/src/Collapsible/ExpertAdviceCollapsible/index.ts @@ -0,0 +1 @@ +export * from "./ExpertAdviceCollapsible" diff --git a/packages/components/src/Collapsible/types.ts b/packages/components/src/Collapsible/types.ts new file mode 100644 index 00000000000..a483b5eae2b --- /dev/null +++ b/packages/components/src/Collapsible/types.ts @@ -0,0 +1,3 @@ +export type Sticky = { + top: string +} diff --git a/yarn.lock b/yarn.lock index bd2f64f2b3f..da9d4ac056c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16533,6 +16533,11 @@ raw-loader@^4.0.2: loader-utils "^2.0.0" schema-utils "^3.0.0" +react-animate-height@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-animate-height/-/react-animate-height-3.2.2.tgz#ae15d3ef6396f45140c60af4081d43d7dd093cda" + integrity sha512-uUOS+RhYVgyJEWcuAJgelVwhcJ2chsMk7HZCpu+wtjSlFAGSFsHU0r4lMTt47HQ1RdQfI5MmFRt43yHTP9lfmQ== + react-clientside-effect@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz#29f9b14e944a376b03fb650eed2a754dd128ea3a"