Repository: marigold-ui/marigold
Pull request #1876 (2022-03-18 12:29:12)

refa(docs): use image correctly in mdx
#1873 

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 16456f725b892366d7aa4b4ff85aed931b441d7a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/marigold-ui/marigold/new/rename-icon-image?filename=.changeset/honest-goats-count.md&value=---%0A%22%40marigold%2Fdocs%22%3A%20patch%0A---%0A%0Arefa(docs)%3A%20use%20image%20correctly%20in%20mdx%0A)



Patch:
diff --git a/docs/content/components/aside.mdx b/docs/content/components/aside.mdx
new file mode 100644
index 000000000..c71bea6be
--- /dev/null
+++ b/docs/content/components/aside.mdx
@@ -0,0 +1,57 @@
+---
+title: Aside
+---
+
+# Aside
+
+## Demo
+
+<MarigoldTheme>
+  <Aside space="medium">
+    <Box border="1px solid #ced4da" bg="#e9ecef" height="100px" width="50px" />
+    <Box border="1px solid #ced4da" bg="#e9ecef" />
+  </Aside>
+</MarigoldTheme>
+
+## Description
+
+The `<Aside>` layout is useful when you want to set two things next to each other and the one has a fixed width (the aside) and the other can have a dynamic width (the content).
+
+It is important to note that the `<Aside>` must have exactly too children, where none is a `<Fragment>`.
+
+## Properties
+
+| Property               | Type                           | Default |
+| :--------------------- | :----------------------------- | :------ |
+| `space` (optional)     | `ResponsiveStyleValue<string>` | `none`  |
+| `side` (optional)      | `left`, `right`                | `left`  |
+| `sideWidth` (optional) | `ResponsiveStyleValue<string>` |         |
+| `stretch` (optional)   | boolean                        | `true`  |
+| `wrap` (optional)      | `Non-zero Percentage`          | `50%`   |
+
+## Import
+
+```tsx onlyCode
+import { Aside } from '@marigold/components';
+```
+
+## Types
+
+### Spacing
+
+```tsx
+  <Aside space="medium">
+    <Box border="1px solid #ced4da" bg="#e9ecef" height="100px" width="50px" />
+    <Box border="1px solid #ced4da" bg="#e9ecef" />
+  </Aside>
+```
+
+### Aside on the right
+
+```tsx
+  <Aside space="medium" side="right">
+    <Box border="1px solid #ced4da" bg="#e9ecef" />
+    <Box border="1px solid #ced4da" bg="#e9ecef" height="100px" width="50px" />
+  </Aside>
+```
+
diff --git a/docs/content/components/image.mdx b/docs/content/components/image.mdx
index b0a2a5c4b..2b9ec0880 100644
--- a/docs/content/components/image.mdx
+++ b/docs/content/components/image.mdx
@@ -2,8 +2,6 @@
 title: Image
 ---
 
-import { Logo } from '../../src/components/Logo/Logo';
-
 # Image
 
 ## Description
@@ -25,8 +23,9 @@ import { Image } from '@marigold/components';
 
 ## Usage
 
-<Logo />
-
-```tsx onlyCode
-<Image src="./marigoldLogo" alt="marigold_logo" />
+```tsx expandCode
+<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"
+/>
 ```
diff --git a/packages/components/src/Aside/Aside.stories.tsx b/packages/components/src/Aside/Aside.stories.tsx
new file mode 100644
index 000000000..edcd2b1cc
--- /dev/null
+++ b/packages/components/src/Aside/Aside.stories.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import type { Meta, ComponentStory } from '@storybook/react';
+import { Box } from '../Box';
+
+import { Aside } from './Aside';
+
+export default {
+  title: 'Components/Aside',
+  argTypes: {
+    space: {
+      control: {
+        type: 'select',
+      },
+      options: [
+        'none',
+        'xxsmall',
+        'xsmall',
+        'small',
+        'medium',
+        'large',
+        'xlarge',
+        'xxlarge',
+      ],
+      defaultValue: 'medium',
+      description: 'Value representing the space between the two elements',
+    },
+    side: {
+      control: {
+        type: 'select',
+      },
+      options: ['left', 'right'],
+      defaultValue: 'left',
+      description: 'Which element to treat as the sidebar',
+    },
+    stretch: {
+      control: {
+        type: 'boolean',
+      },
+      defaultValue: true,
+      description: 'Make the adjacent elements adopt their natural height',
+    },
+    sideWidth: {
+      control: {
+        type: 'text',
+      },
+      defaultValue: 'xxlarge',
+      description: `Represents the width of the sidebar when adjacent. If not set (undefined) it defaults to the sidebar's content width`,
+    },
+    wrap: {
+      control: {
+        type: 'text',
+      },
+      defaultValue: '50%',
+      description:
+        'The narrowest the content (main) element can be before wrapping. Should be a percentage.',
+    },
+  },
+} as Meta;
+
+export const Basic: ComponentStory<typeof Aside> = args => (
+  <Aside {...args}>
+    <Box css={{ bg: '#f1f3f5' }}>
+      Ketchup was once sold as medicine. The condiment was prescribed and sold
+      to people suffering with indigestion back in 1834.
+    </Box>
+    <Box css={{ bg: '#f1f3f5' }}>
+      There is actually a word for someone giving an opinion on something they
+      know nothing about. An 'ultracrepidarian' is someone who voices thoughts
+      beyond their expertise.
+    </Box>
+  </Aside>
+);
+
+export const InheritWidth: ComponentStory<typeof Aside> = () => (
+  <Aside space="large">
+    <img
+      src="https://images.dog.ceo/breeds/pug/n02110958_13993.jpg"
+      alt="Pug"
+      width="250px"
+    />
+    <Box css={{ bg: '#f1f3f5' }}>
+      Pugs were originally bred to be lapdogs for Chinese royalty. They lived in
+      luxury with the emperor, his family, and members of the imperial court.
+      They were royal and loyal companions, and were highly valued in society.
+    </Box>
+  </Aside>
+);
diff --git a/packages/components/src/Aside/Aside.tsx b/packages/components/src/Aside/Aside.tsx
new file mode 100644
index 000000000..7d8d8a269
--- /dev/null
+++ b/packages/components/src/Aside/Aside.tsx
@@ -0,0 +1,57 @@
+import React, { ReactChild } from 'react';
+import { ResponsiveStyleValue } from '@marigold/system';
+import { NonZeroPercentage } from '@marigold/types';
+
+import { Box } from '../Box';
+
+export interface AsideProps {
+  children: [ReactChild, ReactChild];
+  side?: 'left' | 'right';
+  sideWidth?: ResponsiveStyleValue<string>;
+  space?: ResponsiveStyleValue<string>;
+  stretch?: boolean;
+  wrap?: NonZeroPercentage;
+}
+
+/**
+ * Apply CSS depending on which element should serve as sidebar.
+ */
+const SIDE_MAP = {
+  left: [':not(style):first-of-type', ':last-child'],
+  right: [':last-child', ':not(style):first-of-type'],
+};
+
+export const Aside = ({
+  children,
+  sideWidth,
+  space = 'none',
+  side = 'left',
+  stretch = true,
+  wrap = '50%',
+}: AsideProps) => {
+  const [aside, content] = SIDE_MAP[side];
+
+  return (
+    <Box
+      css={{
+        display: 'flex',
+        flexWrap: 'wrap',
+        gap: space,
+        alignItems: stretch ? undefined : 'flex-start',
+
+        [`> ${aside}`]: {
+          flexBasis: sideWidth,
+          flexGrow: 1,
+        },
+
+        [`> ${content}`]: {
+          flexBasis: 0,
+          flexGrow: 999,
+          minInlineSize: wrap,
+        },
+      }}
+    >
+      {children}
+    </Box>
+  );
+};
diff --git a/packages/components/src/Aside/index.ts b/packages/components/src/Aside/index.ts
new file mode 100644
index 000000000..123e2ace6
--- /dev/null
+++ b/packages/components/src/Aside/index.ts
@@ -0,0 +1,1 @@
+export * from './Aside';
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index 4f8d06b90..d18fe4208 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -1,6 +1,7 @@
 export * from './theme';
 
 export * from './ActionGroup';
+export * from './Aside';
 export * from './Badge';
 export * from './Box';
 export * from './Button';
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 718ed0610..174ac2b32 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -1,5 +1,14 @@
 import React from 'react';
 
+export type OneToNine = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
+export type ZeroToNine = 0 | OneToNine;
+
+/**
+ * Percentage value.
+ */
+export type Percentage = `${ZeroToNine}%` | `${OneToNine}${ZeroToNine}%`;
+export type NonZeroPercentage = `${OneToNine}%` | `${OneToNine}${ZeroToNine}%`;
+
 /**
  * Get values of an `array` as literals.
  */
@@ -67,8 +76,8 @@ export type PolymorphicPropsWithRef<P, T extends React.ElementType> = Merge<
 
 /**
  * Create a polymorphpic component with the `as` prop.
- * 
- * This type can be used to create a component that allows to change the rendered 
+ *
+ * This type can be used to create a component that allows to change the rendered
  * element via its `as` prop.
  *
  * @example
@@ -87,8 +96,8 @@ export interface PolymorphicComponent<P, T extends React.ElementType>
 
 /**
  * Create a polymorphpic component with the `as` prop that also support passing a `ref`.
- * 
- * This type can be used to create a component that allows to change the rendered 
+ *
+ * This type can be used to create a component that allows to change the rendered
  * element via its `as` prop and forwarding a `ref` to it.
  *
  * **Note: you should use React's `forwardRef` with this type.**
@@ -105,4 +114,4 @@ export interface PolymorphicComponentWithRef<P, T extends React.ElementType>
   <As extends React.ElementType>(
     props: PolymorphicPropsWithRef<P, As>
   ): React.ReactElement<P> | null;
-}
\ No newline at end of file
+}


Test patch:
diff --git a/packages/components/src/Aside/Aside.test.tsx b/packages/components/src/Aside/Aside.test.tsx
new file mode 100644
index 000000000..40a89fb13
--- /dev/null
+++ b/packages/components/src/Aside/Aside.test.tsx
@@ -0,0 +1,187 @@
+/* eslint-disable testing-library/no-node-access */
+import React from 'react';
+import { ThemeProvider } from '@marigold/system';
+import { render, screen } from '@testing-library/react';
+
+import { Aside } from './Aside';
+
+// Setup
+// ---------------
+const theme = {
+  space: {
+    none: 0,
+    small: 2,
+    medium: 4,
+    large: 8,
+  },
+  sizes: {
+    none: 0,
+    small: 250,
+    medium: 350,
+    large: 500,
+  },
+};
+
+test('default spacing is "none"', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside>
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+
+  const aside = screen.getByText(/aside/).parentElement;
+  expect(aside).toHaveStyle(`gap: 0`);
+});
+
+test('accepts and uses spacing from theme', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside space="medium">
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+  const aside = screen.getByText(/aside/).parentElement;
+  expect(aside).toHaveStyle(`gap: 4px`);
+});
+
+test('aside is on the left by default', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside>
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+
+  const aside = screen.getByText(/aside/);
+  const content = screen.getByText(/content/);
+  expect(aside).toHaveStyle('flex-grow: 1');
+  expect(content).toHaveStyle('flex-grow: 999');
+});
+
+test('allows to have aisde on the right', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside side="right">
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+
+  const aside = screen.getByText(/aside/);
+  const content = screen.getByText(/content/);
+  expect(aside).toHaveStyle('flex-grow: 999');
+  expect(content).toHaveStyle('flex-grow: 1');
+});
+
+test('inherits asides children with by default', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside>
+        <div style={{ width: 50 }}>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+  const aside = screen.getByText(/aside/);
+  expect(aside).toHaveStyle(`width: 50px`);
+  expect(aside).not.toHaveStyle(`flex-basis: 50px`);
+});
+
+test('allows to set a width for the aside element', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside sideWidth="200px">
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+  const aside = screen.getByText(/aside/);
+  expect(aside).toHaveStyle(`flex-basis: 200px`);
+});
+
+test('streches content by default', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside>
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+  const parent = screen.getByText(/content/).parentElement;
+  expect(parent).not.toHaveStyle('align-items: flex-start');
+});
+
+test('allows to disabled content stretching', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside stretch={false}>
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+  const parent = screen.getByText(/content/).parentElement;
+  expect(parent).toHaveStyle('align-items: flex-start');
+});
+
+test('wraps at 50% by default', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside>
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+  const content = screen.getByText(/content/);
+  expect(content).toHaveStyle('minInlineSize: 50%');
+});
+
+test('allows to customize wrapping', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside wrap="24%">
+        <div>aside</div>
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+  const content = screen.getByText(/content/);
+  expect(content).toHaveStyle('minInlineSize: 24%');
+});
+
+test('works with SSR', () => {
+  // Fake emotions SSR rendering, where emotion inlines styles
+  const SSRComponent = () => (
+    <>
+      <style data-testid="ssr-style">{`.ssr { background: 'hotpink' }`}</style>
+      <span data-testid="actual-element">aside</span>
+    </>
+  );
+
+  render(
+    <ThemeProvider theme={theme}>
+      <Aside space="medium" wrap="1%">
+        <SSRComponent />
+        <div>content</div>
+      </Aside>
+    </ThemeProvider>
+  );
+
+  const style = screen.getByTestId('ssr-style');
+  const aside = screen.getByTestId('actual-element');
+
+  // Yes, this tests implementation details, but I can not think of another way to test this
+  expect(style).not.toHaveStyle('flex-grow: 1');
+  expect(aside).toHaveStyle('flex-grow: 1');
+});