Skip to content

Commit

Permalink
Merge pull request #6919 from Sage/FE-6423
Browse files Browse the repository at this point in the history
feat(preview): add `shape` and `disableAnimation` props
  • Loading branch information
nuria1110 committed Sep 16, 2024
2 parents 15ad631 + bd071fe commit bc11f36
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 101 deletions.
6 changes: 4 additions & 2 deletions src/components/link-preview/link-preview.style.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import styled, { css } from "styled-components";
import { StyledPreview } from "../preview/preview.style";
import { StyledPreviewPlaceholder } from "../preview/__internal__/preview-placeholder.style";
import {
StyledPreview,
StyledPreviewPlaceholder,
} from "../preview/preview.style";
import addFocusStyling from "../../style/utils/add-focus-styling";
import baseTheme from "../../style/themes/base";

Expand Down

This file was deleted.

28 changes: 0 additions & 28 deletions src/components/preview/__internal__/preview-placeholder.style.ts

This file was deleted.

15 changes: 6 additions & 9 deletions src/components/preview/preview-test.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from "react";
import { Meta, StoryObj } from "@storybook/react";
import Preview from "./preview.component";

export default {
const meta: Meta<typeof Preview> = {
title: "Preview/Test",
component: Preview,
parameters: {
info: { disable: true },
chromatic: {
Expand All @@ -11,17 +12,13 @@ export default {
},
};

export const Default = ({ children, ...args }: { children?: string }) => (
<Preview {...args}>{children}</Preview>
);
export default meta;
type Story = StoryObj<typeof Preview>;

Default.story = {
name: "default",
export const Default: Story = {
args: {
children: "Text rendered as children component.",
height: "",
lines: 1,
loading: true,
width: "",
},
};
51 changes: 37 additions & 14 deletions src/components/preview/preview.component.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,63 @@
import React from "react";
import { MarginProps } from "styled-system";

import PreviewPlaceholder, {
PreviewPlaceholderProps,
} from "./__internal__/preview-placeholder.component";
import { StyledPreview } from "./preview.style";
import { StyledPreview, StyledPreviewPlaceholder } from "./preview.style";
import { filterStyledSystemMarginProps } from "../../style/utils";
import useMediaQuery from "../../hooks/useMediaQuery";

export type Shapes = "text" | "rectangle" | "rectangle-round" | "circle";

export interface PreviewProps
extends Partial<Omit<PreviewPlaceholderProps, "index">>,
MarginProps {
export interface PreviewProps extends MarginProps {
/** Children content to render in the component. */
children?: React.ReactNode;
/** Provides more control over when in a loading state. */
/** Sets loading state. */
loading?: boolean;
/** Sets the height of the Preview. */
height?: string;
/** Sets the width of the Preview. */
width?: string;
/** The number of placeholder shapes to render. */
lines?: number;
/** Sets the preview's shape. */
shape?: Shapes;
/** Removes Preview's animation, is true when prefer reduce-motion is on. */
disableAnimation?: boolean;
}

export const Preview = ({
children,
loading,
lines = 1,
height,
width,
shape = "text",
disableAnimation,
...props
}: PreviewProps) => {
const marginProps = filterStyledSystemMarginProps(props);
const hasPlaceholder = loading === undefined ? !children : loading;
const hasPlaceholder = loading ?? !children;

const isLastLine = (index: number) => {
return lines > 1 && lines === index + 1;
};

const reduceMotion = !useMediaQuery(
"screen and (prefers-reduced-motion: no-preference)"
);

if (hasPlaceholder) {
const placeholders = [];

for (let i = 1; i <= lines; i++) {
for (let i = 0; i < lines; i++) {
placeholders.push(
<PreviewPlaceholder
<StyledPreviewPlaceholder
data-component="preview"
data-role="preview-placeholder"
key={i}
index={i}
lines={lines}
height={height}
width={width}
isLastLine={isLastLine(i)}
shape={shape}
disableAnimation={disableAnimation || reduceMotion}
{...props}
/>
);
Expand Down
31 changes: 27 additions & 4 deletions src/components/preview/preview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,45 @@ import Preview from "carbon-react/lib/components/preview";

<Canvas of={PreviewStories.Default} />

### Preview with Lines
### With Lines

You can use the `lines` prop to specify the number of placeholder shapes to render.

<Canvas of={PreviewStories.WithLines} />

### Preview with Children
### With Children

You can pass children to the component which will render when the `loading` prop is `false` or undefined.

<Canvas of={PreviewStories.WithChildren} />

### Preview with Custom Width
### With Custom Width

You can use the `width` prop to specify the width of the Preview.

<Canvas of={PreviewStories.WithWidth} />

### Preview with Custom Height
### With Custom Height

You can use the `height` prop to specify the height of the Preview.

<Canvas of={PreviewStories.WithHeight} />

### Shapes

By default, the shape of the Preview is "text", however you can use the `shape` prop to change this.
You may also use the `lines` prop to render multiple Previews with the specified shape and the `width` and `height` props to change the default dimensions.

Note that when the `shape` prop is set to "circle", the `height` prop will determine the diameter and the `width` prop will be ignored.

<Canvas of={PreviewStories.Shapes} />

### Disable Animation

You can set the `disableAnimation` prop to true to disable the loading animation. This will automatically be set to true when prefer reduce-motion is enabled.

<Canvas of={PreviewStories.DisableAnimation} />

## Props

### Preview
Expand Down
13 changes: 12 additions & 1 deletion src/components/preview/preview.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ test.describe("check Preview component properties", () => {
expect(elementsCount).toBe(line);
});
});

test("should render with no animation when the user prefers reduced motion", async ({
mount,
page,
}) => {
await page.emulateMedia({ reducedMotion: "reduce" });

await mount(<Preview />);

await expect(lineComponent(page)).toHaveCSS("animation-name", "none");
});
});

test.describe("Border radius", () => {
Expand All @@ -95,7 +106,7 @@ test.describe("Border radius", () => {
}) => {
await mount(<Preview />);

await expect(lineComponent(page)).toHaveCSS("border-radius", "4px");
await expect(lineComponent(page)).toHaveCSS("border-radius", "8px");
});

test("should have the expected styling when roundedCornersOptOut is true", async ({
Expand Down
18 changes: 17 additions & 1 deletion src/components/preview/preview.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Meta, StoryObj } from "@storybook/react";
import generateStyledSystemProps from "../../../.storybook/utils/styled-system-props";

import Button from "../button";
import Preview from "./preview.component";
import Preview from ".";

const styledSystemProps = generateStyledSystemProps({
margin: true,
Expand Down Expand Up @@ -58,3 +58,19 @@ export const WithHeight: Story = () => {
return <Preview loading height="256px" />;
};
WithHeight.storyName = "With Height";

export const Shapes: Story = () => {
return (
<>
<Preview mb={2} loading shape="rectangle" />
<Preview mb={2} loading shape="rectangle-round" />
<Preview loading shape="circle" />
</>
);
};
Shapes.storyName = "Shapes";

export const DisableAnimation: Story = () => {
return <Preview loading disableAnimation />;
};
DisableAnimation.storyName = "Disable Animation";
95 changes: 92 additions & 3 deletions src/components/preview/preview.style.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,103 @@
import styled from "styled-components";
import styled, { css, keyframes } from "styled-components";
import { margin } from "styled-system";
import baseTheme from "../../style/themes/base";
import { Shapes } from "./preview.component";

const StyledPreview = styled.div`
${margin}
`;

interface StyledPreviewPlaceholderProps {
height?: string;
width?: string;
shape: Shapes;
disableAnimation?: boolean;
isLastLine: boolean;
}

const shimmer = keyframes`
0% {
opacity: 0.1
}
70% {
opacity: 1
}
100% {
opacity: 0.1
}
`;

function getBorderRadius(shape: Shapes) {
switch (shape) {
case "rectangle-round":
return "var(--borderRadius400)";
case "circle":
return "var(--borderRadiusCircle)";
default:
return "var(--borderRadius100)";
}
}

function getHeight(shape: Shapes) {
if (shape.includes("rectangle")) {
return "var(--sizing400)";
}

switch (shape) {
case "circle":
return "var(--sizing700)";
default:
return "var(--sizing175)";
}
}

function getWidth(shape: Shapes) {
if (shape.includes("rectangle")) {
return "var(--sizing1500)";
}

return "100%";
}

const StyledPreviewPlaceholder = styled.span<StyledPreviewPlaceholderProps>`
${({ shape, disableAnimation, isLastLine, height, width }) => {
return css`
display: block;
background: linear-gradient(
135deg,
var(--colorsUtilityMajor100),
var(--colorsUtilityMajor040)
);
border-radius: ${getBorderRadius(shape)};
height: ${height || getHeight(shape)};
width: ${width || getWidth(shape)};
animation: ${shimmer} 2s ease infinite;
${isLastLine &&
shape === "text" &&
css`
width: calc(${width || getWidth(shape)}*0.8);
`}
${shape === "circle" &&
css`
width: ${height || getHeight(shape)};
`}
${disableAnimation &&
css`
animation: none;
`}
& + & {
margin-top: 6px;
}
`;
}}
`;

StyledPreview.defaultProps = {
theme: baseTheme,
};

// eslint-disable-next-line import/prefer-default-export
export { StyledPreview };
export { StyledPreview, StyledPreviewPlaceholder };
Loading

0 comments on commit bc11f36

Please sign in to comment.