Add more functionalities to our playground
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { ButtonProps, Text } from '@backstage/canon';
|
||||
import { Stack, Inline, Button } from '@backstage/canon';
|
||||
|
||||
const variants: {
|
||||
variant: ButtonProps['variant'];
|
||||
size: ButtonProps['size'];
|
||||
}[] = [
|
||||
{ variant: 'primary', size: 'small' },
|
||||
{ variant: 'primary', size: 'medium' },
|
||||
{ variant: 'secondary', size: 'small' },
|
||||
{ variant: 'secondary', size: 'medium' },
|
||||
{ variant: 'tertiary', size: 'small' },
|
||||
{ variant: 'tertiary', size: 'medium' },
|
||||
];
|
||||
|
||||
const capitalizeFirstLetter = (string: string): string => {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
};
|
||||
|
||||
export const ButtonPlayground = () => {
|
||||
return (
|
||||
<Stack>
|
||||
{variants.map(({ variant, size }) => (
|
||||
<Stack key={`${variant}-${size}`}>
|
||||
<Text>
|
||||
{capitalizeFirstLetter(variant as string)} -{' '}
|
||||
{capitalizeFirstLetter(size as string)}
|
||||
</Text>
|
||||
<Inline alignY="center">
|
||||
<Button iconStart="cloud" variant={variant} size={size}>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconEnd="chevronRight" variant={variant} size={size}>
|
||||
Button
|
||||
</Button>
|
||||
<Button
|
||||
iconStart="cloud"
|
||||
iconEnd="chevronRight"
|
||||
style={{ width: '200px' }}
|
||||
variant={variant}
|
||||
size={size}
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
</Inline>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Text } from '@backstage/canon';
|
||||
import { Stack, Inline, Checkbox } from '@backstage/canon';
|
||||
|
||||
export const CheckboxPlayground = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<Stack>
|
||||
<Text>All variants</Text>
|
||||
<Inline alignY="center">
|
||||
<Checkbox />
|
||||
<Checkbox checked />
|
||||
<Checkbox label="Checkbox" />
|
||||
<Checkbox label="Checkbox" checked />
|
||||
</Inline>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -1,15 +1,32 @@
|
||||
'use client';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
import { Text } from '@backstage/canon';
|
||||
import { Inline, Stack, Text } from '@backstage/canon';
|
||||
import { screenSizes } from '@/utils/data';
|
||||
import { Frame } from '@/components/Frame';
|
||||
import { Stack, Inline, Button } from '@backstage/canon';
|
||||
import { usePlayground } from '@/utils/playground-context';
|
||||
import { ButtonPlayground } from './button';
|
||||
import { CheckboxPlayground } from './checkbox';
|
||||
import styles from './styles.module.css';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function PlaygroundPage() {
|
||||
const { selectedScreenSizes, selectedComponents } = usePlayground();
|
||||
|
||||
const filteredScreenSizes = screenSizes.filter(item =>
|
||||
selectedScreenSizes.includes(item.slug),
|
||||
);
|
||||
|
||||
if (filteredScreenSizes.length === 0) {
|
||||
return (
|
||||
<div className={styles.containerEmpty}>
|
||||
<Content />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{screenSizes.map(screenSize => (
|
||||
{filteredScreenSizes.map(screenSize => (
|
||||
<div
|
||||
className={styles.breakpointContainer}
|
||||
style={{ width: screenSize.width }}
|
||||
@@ -20,27 +37,7 @@ export default function PlaygroundPage() {
|
||||
</Text>
|
||||
<div className={styles.breakpointContent}>
|
||||
<Frame>
|
||||
<Stack>
|
||||
<Stack>
|
||||
<Text>Button - Primary - Small</Text>
|
||||
<Inline alignY="center">
|
||||
<Button iconStart="cloud">Button</Button>
|
||||
<Button iconEnd="chevronRight">Button</Button>
|
||||
<Button iconStart="cloud" iconEnd="chevronRight">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart="cloud" iconEnd="chevronRight">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart="cloud" iconEnd="chevronRight">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart="cloud" iconEnd="chevronRight">
|
||||
Button
|
||||
</Button>
|
||||
</Inline>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Content />
|
||||
</Frame>
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,3 +45,29 @@ export default function PlaygroundPage() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Content = () => {
|
||||
const { selectedComponents } = usePlayground();
|
||||
|
||||
return (
|
||||
<Stack gap="xl">
|
||||
{selectedComponents.find(c => c === 'button') && (
|
||||
<Line content={<ButtonPlayground />} title="Button" />
|
||||
)}
|
||||
{selectedComponents.find(c => c === 'checkbox') && (
|
||||
<Line content={<CheckboxPlayground />} title="Checkbox" />
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const Line = ({ content, title }: { content: ReactNode; title: string }) => {
|
||||
return (
|
||||
<Inline gap={{ xs: 'xs', md: 'xl' }}>
|
||||
<div style={{ width: '100px' }}>
|
||||
<Text>{title}</Text>
|
||||
</div>
|
||||
{content}
|
||||
</Inline>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.containerEmpty {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.breakpointContainer {
|
||||
flex-shrink: 0;
|
||||
margin-top: 40px;
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { CanonProvider } from '@backstage/canon';
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import { PlaygroundProvider } from '@/utils/playground-context';
|
||||
|
||||
export const Providers = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<CanonProvider>
|
||||
<ThemeProvider defaultTheme="light">{children}</ThemeProvider>
|
||||
<ThemeProvider defaultTheme="light">
|
||||
<PlaygroundProvider>{children}</PlaygroundProvider>
|
||||
</ThemeProvider>
|
||||
</CanonProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar svg path {
|
||||
.logo path {
|
||||
fill: var(--canon-text-primary);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ export const Sidebar = () => {
|
||||
height="27"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={styles.logo}
|
||||
>
|
||||
<path d="M77.414 9.71h3.036v2.992l-.264-.022c.279-.88.748-1.65 1.408-2.31.675-.66 1.533-.99 2.574-.99 1.086 0 1.9.352 2.442 1.056.558.689.836 1.598.836 2.728v7.502H84.41v-6.6c0-.66-.117-1.159-.352-1.496-.234-.338-.623-.506-1.166-.506-.601 0-1.122.271-1.562.814a4.321 4.321 0 0 0-.88 1.848v5.94h-3.036V9.71ZM69.868 21.04c-1.13 0-2.142-.257-3.036-.77a5.547 5.547 0 0 1-2.068-2.09c-.484-.895-.726-1.892-.726-2.992 0-1.115.242-2.112.726-2.992a5.503 5.503 0 0 1 2.068-2.112c.88-.514 1.892-.77 3.036-.77 1.144 0 2.156.256 3.036.77a5.347 5.347 0 0 1 2.046 2.112c.498.88.748 1.87.748 2.97 0 1.114-.25 2.12-.748 3.014a5.343 5.343 0 0 1-2.068 2.09c-.88.513-1.885.77-3.014.77Zm0-2.618c.557 0 1.048-.132 1.474-.396a2.79 2.79 0 0 0 .99-1.144c.234-.499.352-1.064.352-1.694 0-.63-.118-1.188-.352-1.672a2.764 2.764 0 0 0-.99-1.166c-.426-.279-.917-.418-1.474-.418-.558 0-1.05.14-1.474.418a2.764 2.764 0 0 0-.99 1.166c-.22.484-.33 1.041-.33 1.672 0 .63.11 1.195.33 1.694.234.484.564.865.99 1.144.425.264.916.396 1.474.396ZM52.385 9.71h3.036v2.992l-.264-.022c.279-.88.748-1.65 1.408-2.31.675-.66 1.533-.99 2.574-.99 1.085 0 1.9.352 2.442 1.056.557.689.836 1.598.836 2.728v7.502h-3.036v-6.6c0-.66-.117-1.159-.352-1.496-.235-.338-.623-.506-1.166-.506-.601 0-1.122.271-1.562.814a4.321 4.321 0 0 0-.88 1.848v5.94h-3.036V9.71ZM43.904 20.952c-1.026 0-1.87-.308-2.53-.924-.66-.616-.99-1.416-.99-2.398 0-1.115.418-2.01 1.254-2.684.85-.69 2.105-1.034 3.762-1.034h2.75l-.924.352v-.462c0-.572-.176-1.02-.528-1.342-.352-.338-.931-.506-1.738-.506-.69 0-1.386.154-2.09.462-.704.308-1.29.69-1.76 1.144v-2.882c.47-.352 1.1-.66 1.892-.924a7.942 7.942 0 0 1 2.486-.396c1.599 0 2.78.41 3.542 1.232.778.806 1.166 1.914 1.166 3.322v5.742c0 .176.008.352.022.528.03.161.066.322.11.484h-3.014v-3.3l.044 1.386a4.044 4.044 0 0 1-1.32 1.606c-.572.396-1.283.594-2.134.594Zm.858-2.156c.558 0 1.049-.206 1.474-.616.44-.426.77-.954.99-1.584v-.792h-1.562c-.762 0-1.334.146-1.716.44-.381.278-.572.674-.572 1.188 0 .41.125.74.374.99.264.25.602.374 1.012.374ZM34.66 21.04c-1.525 0-2.874-.345-4.047-1.034-1.174-.704-2.09-1.68-2.75-2.926-.646-1.247-.968-2.662-.968-4.246 0-1.584.322-2.992.968-4.224.66-1.232 1.576-2.193 2.75-2.882 1.173-.704 2.522-1.056 4.048-1.056.968 0 1.818.11 2.552.33.748.22 1.356.506 1.826.858v3.498c-.396-.484-.96-.895-1.694-1.232-.719-.338-1.51-.506-2.376-.506-.939 0-1.775.22-2.508.66-.719.44-1.284 1.056-1.694 1.848-.396.792-.594 1.694-.594 2.706 0 1.026.198 1.943.594 2.75.41.792.975 1.408 1.694 1.848.733.44 1.57.66 2.508.66.88 0 1.68-.162 2.398-.484.718-.323 1.276-.726 1.672-1.21v3.41a5.686 5.686 0 0 1-1.87.902c-.704.22-1.54.33-2.508.33ZM11.275 6.271c.276.526.019 1.152-.464 1.498a5.846 5.846 0 0 0 1.004 10.082c.454.205.789.632.789 1.13v6.206a.837.837 0 0 1-.838.837C5.33 26.024.112 20.804.112 14.366V.862C.112.399.487.024.95.024c4.483 0 8.376 2.533 10.325 6.247Z" />
|
||||
<path d="M14.037 16.729a4.237 4.237 0 0 0 4.234-4.24 4.237 4.237 0 0 0-4.234-4.242 4.237 4.237 0 0 0-4.234 4.241 4.237 4.237 0 0 0 4.234 4.24Z" />
|
||||
|
||||
@@ -5,11 +5,38 @@ import { Box, Checkbox, Text } from '@backstage/canon';
|
||||
import { motion } from 'framer-motion';
|
||||
import styles from './Sidebar.module.css';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { screenSizes } from '@/utils/data';
|
||||
import { screenSizes, ScreenSize } from '@/utils/data';
|
||||
import { usePlayground } from '@/utils/playground-context';
|
||||
|
||||
export const Playground = () => {
|
||||
const pathname = usePathname();
|
||||
const isPlayground = pathname.includes('/playground');
|
||||
const {
|
||||
selectedScreenSizes,
|
||||
setSelectedScreenSizes,
|
||||
selectedComponents,
|
||||
setSelectedComponents,
|
||||
} = usePlayground();
|
||||
|
||||
const handleComponentCheckboxChange = (slug: string) => {
|
||||
if (selectedComponents.find(item => item === slug)) {
|
||||
const res = selectedComponents.filter(item => item !== slug);
|
||||
setSelectedComponents(res);
|
||||
} else {
|
||||
setSelectedComponents([...selectedComponents, slug]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (slug: string) => {
|
||||
if (selectedScreenSizes.find(item => item === slug)) {
|
||||
const res = selectedScreenSizes.filter(item => item !== slug);
|
||||
setSelectedScreenSizes(res);
|
||||
} else {
|
||||
setSelectedScreenSizes([...selectedScreenSizes, slug]);
|
||||
}
|
||||
};
|
||||
|
||||
console.log(selectedComponents);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
@@ -35,7 +62,10 @@ export const Playground = () => {
|
||||
{components.map(({ slug, title }) => (
|
||||
<div className={styles.line} key={slug}>
|
||||
<Text variant="body">{title}</Text>
|
||||
<Checkbox />
|
||||
<Checkbox
|
||||
checked={selectedComponents.includes(slug)}
|
||||
onChange={() => handleComponentCheckboxChange(slug)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Box marginTop="md" marginBottom="xs">
|
||||
@@ -46,7 +76,10 @@ export const Playground = () => {
|
||||
{screenSizes.map(({ slug, title }) => (
|
||||
<div className={styles.line} key={slug}>
|
||||
<Text variant="body">{title}</Text>
|
||||
<Checkbox />
|
||||
<Checkbox
|
||||
checked={selectedScreenSizes.includes(slug)}
|
||||
onChange={() => handleCheckboxChange(slug)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
@@ -71,7 +71,13 @@ export const components = [
|
||||
},
|
||||
];
|
||||
|
||||
export const screenSizes = [
|
||||
export type ScreenSize = {
|
||||
title: string;
|
||||
slug: string;
|
||||
width: number;
|
||||
};
|
||||
|
||||
export const screenSizes: ScreenSize[] = [
|
||||
{ title: 'Mobile', slug: 'mobile', width: 390 },
|
||||
{ title: 'Tablet', slug: 'tablet', width: 768 },
|
||||
{ title: 'Desktop', slug: 'desktop', width: 1280 },
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import React, { createContext, useContext, ReactNode, useState } from 'react';
|
||||
import { components } from './data';
|
||||
|
||||
// Create a context with an empty array as the default value
|
||||
const PlaygroundContext = createContext<{
|
||||
selectedScreenSizes: string[];
|
||||
setSelectedScreenSizes: (screenSizes: string[]) => void;
|
||||
selectedComponents: string[];
|
||||
setSelectedComponents: (components: string[]) => void;
|
||||
}>({
|
||||
selectedScreenSizes: [],
|
||||
setSelectedScreenSizes: () => {},
|
||||
selectedComponents: [],
|
||||
setSelectedComponents: () => {},
|
||||
});
|
||||
|
||||
// Create a provider component
|
||||
export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [selectedScreenSizes, setSelectedScreenSizes] = useState<string[]>([]);
|
||||
const [selectedComponents, setSelectedComponents] = useState<string[]>(
|
||||
components.map(component => component.slug),
|
||||
);
|
||||
|
||||
return (
|
||||
<PlaygroundContext.Provider
|
||||
value={{
|
||||
selectedScreenSizes,
|
||||
setSelectedScreenSizes,
|
||||
selectedComponents,
|
||||
setSelectedComponents,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</PlaygroundContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// Create a custom hook to use the screen sizes
|
||||
export const usePlayground = () => {
|
||||
return useContext(PlaygroundContext);
|
||||
};
|
||||
@@ -29,6 +29,8 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
iconStart,
|
||||
iconEnd,
|
||||
children,
|
||||
style,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const { getResponsiveValue } = useCanon();
|
||||
@@ -39,7 +41,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
{...rest}
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
className={[
|
||||
@@ -47,6 +49,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
`cn-button-${responsiveSize}`,
|
||||
`cn-button-${responsiveVariant}`,
|
||||
].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<span
|
||||
className={[
|
||||
|
||||
@@ -35,18 +35,18 @@
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-outline-focus);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-focus);
|
||||
color: var(--canon-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.cn-button-secondary {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-outline);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-base);
|
||||
color: var(--canon-text-primary);
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-outline-hover);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-hover);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,4 +31,5 @@ export interface ButtonProps {
|
||||
disabled?: boolean;
|
||||
iconStart?: IconNames;
|
||||
iconEnd?: IconNames;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
.table-header {
|
||||
tr {
|
||||
/* border-bottom: 1px solid var(--canon-outline); */
|
||||
/* border-bottom: 1px solid var(--canon-border); */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user