Repository: marigold-ui/marigold
Pull request #1881 (2022-03-21 14:16:43)

Component: Aspect
#1860 


Patch:
diff --git a/.changeset/slimy-walls-film.md b/.changeset/slimy-walls-film.md
new file mode 100644
index 000000000..a245e0471
--- /dev/null
+++ b/.changeset/slimy-walls-film.md
@@ -0,0 +1,6 @@
+---
+"@marigold/docs": minor
+"@marigold/components": minor
+---
+
+Component: Aspect
diff --git a/docs/content/components/aspect.mdx b/docs/content/components/aspect.mdx
new file mode 100644
index 000000000..53c2a909f
--- /dev/null
+++ b/docs/content/components/aspect.mdx
@@ -0,0 +1,75 @@
+---
+title: Aspect
+---
+
+# Aspect
+
+## Description
+
+The Aspect component is used to embed responsive images, videos, maps, etc. You can use a predefined ratio and maximum width.
+
+## Demo
+
+<MarigoldTheme>
+  <Aspect maxWidth="25vw" ratio="portrait">
+    <Image
+      src="https://images.unsplash.com/photo-1603910234616-3b5f4a6be2b4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80"
+      alt="event_image"
+    />
+  </Aspect>
+</MarigoldTheme>
+
+## Properties (API)
+
+| Property   | Type                                                         | Default  |
+| :--------- | :----------------------------------------------------------- | :------- |
+| `ratio`    | `square, landscape, portrait, widescreen, ultrawide, golden` | `square` |
+| `maxWidth` | `string`                                                     | `100%`   |
+
+## Import
+
+```tsx onlyCode
+import { Aspect } from '@marigold/components';
+```
+
+## Variants
+
+### Image - Ratio Square
+
+```tsx
+<Aspect maxWidth="25vw">
+  <Image
+    src="https://images.unsplash.com/photo-1603910234616-3b5f4a6be2b4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80"
+    alt="event_image"
+  />
+</Aspect>
+```
+
+### Map Ratio Landscape
+
+```tsx
+<Aspect ratio="landscape">
+  <iframe
+    src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d4820.000043444012!2d7.826018541821473!3d48.020383262446884!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47911b1e29425703%3A0xbe342117a976e59!2sEuropa-Park%20Stadion!5e1!3m2!1sde!2sde!4v1647595604899!5m2!1sde!2sde"
+    title="sc_map"
+    width="100%"
+    height="100%"
+  />
+</Aspect>
+```
+
+### Video - Ratio Widescreen
+
+```tsx
+<Aspect ratio="widescreen">
+  <iframe
+    title="rx_video"
+    src="https://www.youtube.com/embed/_ZKugGhbjLY"
+    width="100%"
+    height="100%"
+    frameborder="0"
+    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
+    allowfullscreen
+  />
+</Aspect>
+```
diff --git a/packages/components/src/Aspect/Aspect.stories.tsx b/packages/components/src/Aspect/Aspect.stories.tsx
new file mode 100644
index 000000000..e438b9250
--- /dev/null
+++ b/packages/components/src/Aspect/Aspect.stories.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import type { Meta, ComponentStory } from '@storybook/react';
+
+import { Aspect } from './Aspect';
+import { Image } from '../Image';
+
+export default {
+  title: 'Components/Aspect',
+  argTypes: {
+    ratio: {
+      control: {
+        type: 'select',
+      },
+      options: [
+        'square',
+        'landscape',
+        'portrait',
+        'widescreen',
+        'ultrawide',
+        'golden',
+      ],
+      description: 'choose between content and header',
+      table: {
+        defaultValue: {
+          summary: 'square',
+        },
+      },
+    },
+    maxWidth: {
+      control: {
+        type: 'text',
+      },
+      description: 'Maximum width',
+      table: {
+        defaultValue: {
+          summary: '100%',
+        },
+      },
+    },
+  },
+} as Meta;
+
+export const Basic: ComponentStory<typeof Aspect> = args => (
+  <Aspect {...args}>
+    <Image
+      src="https://images.unsplash.com/photo-1603910234616-3b5f4a6be2b4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80"
+      alt="event_image"
+    />
+  </Aspect>
+);
+
+export const Map: ComponentStory<typeof Aspect> = args => (
+  <Aspect maxWidth="500px" {...args}>
+    <iframe
+      src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d4820.000043444012!2d7.826018541821473!3d48.020383262446884!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47911b1e29425703%3A0xbe342117a976e59!2sEuropa-Park%20Stadion!5e1!3m2!1sde!2sde!4v1647595604899!5m2!1sde!2sde"
+      title="map_sc"
+      width="100%"
+      height="100%"
+    />
+  </Aspect>
+);
diff --git a/packages/components/src/Aspect/Aspect.tsx b/packages/components/src/Aspect/Aspect.tsx
new file mode 100755
index 000000000..2a97e5dd3
--- /dev/null
+++ b/packages/components/src/Aspect/Aspect.tsx
@@ -0,0 +1,50 @@
+/**
+ * Based on https://theme-ui.com/components/aspect-ratio
+ */
+import React from 'react';
+
+import { aspect } from '@marigold/tokens';
+import { ComponentProps } from '@marigold/types';
+
+import { Box } from '../Box';
+
+export interface AspectProps extends ComponentProps<'div'> {
+  ratio?: keyof typeof aspect;
+  maxWidth?: string;
+}
+
+export const Aspect: React.FC<AspectProps> = React.forwardRef(
+  function AspectRatio(
+    { ratio = 'square', maxWidth = '100%', children, ...props },
+    ref
+  ) {
+    return (
+      <Box
+        ref={ref}
+        __baseCSS={{
+          position: 'relative',
+          overflow: 'hidden',
+          maxWidth: maxWidth,
+        }}
+      >
+        <Box
+          __baseCSS={{
+            aspectRatio: aspect[ratio],
+          }}
+        />
+        <Box
+          {...props}
+          __baseCSS={{
+            position: 'absolute',
+            top: 0,
+            right: 0,
+            bottom: 0,
+            left: 0,
+          }}
+        >
+          {children}
+        </Box>
+      </Box>
+    );
+  }
+);
diff --git a/packages/components/src/Aspect/index.ts b/packages/components/src/Aspect/index.ts
new file mode 100644
index 000000000..f4563da13
--- /dev/null
+++ b/packages/components/src/Aspect/index.ts
@@ -0,0 +1,1 @@
+export * from './Aspect';
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index d18fe4208..2c1378758 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -2,6 +2,7 @@ export * from './theme';
 
 export * from './ActionGroup';
 export * from './Aside';
+export * from './Aspect';
 export * from './Badge';
 export * from './Box';
 export * from './Button';


Test patch:
diff --git a/packages/components/src/Aspect/Aspect.test.tsx b/packages/components/src/Aspect/Aspect.test.tsx
new file mode 100644
index 000000000..f956fe9b4
--- /dev/null
+++ b/packages/components/src/Aspect/Aspect.test.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+
+import { Aspect } from './Aspect';
+import { Text } from '../Text';
+import { Box } from '../Box';
+
+test('supports default maxWidth', () => {
+  render(
+    <Aspect data-testid="aspect">
+      <Text>sdf</Text>
+    </Aspect>
+  );
+  const aspect = screen.getByTestId(/aspect/);
+  // eslint-disable-next-line testing-library/no-node-access
+  expect(aspect.parentElement).toHaveStyle(`maxWidth: 100%`);
+});
+
+test('supports other maxWidth than default', () => {
+  render(
+    <Aspect maxWidth="50vw" data-testid="aspect">
+      <Text>sdf</Text>
+    </Aspect>
+  );
+  const aspect = screen.getByTestId(/aspect/);
+  // eslint-disable-next-line testing-library/no-node-access
+  expect(aspect.parentElement).toHaveStyle(`maxWidth: 50vw`);
+});
+
+test('supports default ratio', () => {
+  render(
+    <Aspect data-testid="aspect" ratio="ultrawide">
+      <Box />
+    </Aspect>
+  );
+  // Note: jest does not support aspect-ratio
+  const aspect = screen.getByTestId(/aspect/);
+  expect(aspect).toBeDefined();
+});