Skip to content

Commit

Permalink
docs: add slider and radio control renderers (#4394)
Browse files Browse the repository at this point in the history
* docs: allow override of default control renderer, add slider
* docs: add radio group control renderer
* docs: minor changes and Banner page
  • Loading branch information
plagoa authored Oct 23, 2024
1 parent 4e84608 commit e04aaa1
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 49 deletions.
96 changes: 87 additions & 9 deletions apps/docs/src/pages/components/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ import React, { useCallback, useState } from "react";
import jsxToString from "react-element-to-jsx-string";
import { CodeEditor } from "react-live-runner";
import { classes } from "@docs/components/code/Live";
import { css } from "@emotion/css";
import { useData } from "nextra/hooks";
// @ts-ignore
import { themes } from "prism-react-renderer";
import {
HvCheckBox,
HvOption,
HvRadio,
HvRadioGroup,
HvSelect,
HvSlider,
} from "@hitachivantara/uikit-react-core";

type ControlType = "radio" | "slider";

type Control = {
type?: string;
type?: ControlType;
defaultValue?: string;
values?: string[];
};
Expand All @@ -37,16 +43,20 @@ const generateCode = (
.map(([key, value]) =>
typeof value === "boolean" && value
? key
: "boolean" && !value
: typeof value === "boolean" && !value
? ""
: `${key}="${value}"`,
)
.join(" ")
.trim();

const componentPropsString = Object.entries(componentProps || {})
.map(([key, value]) => `${key}="${value}"`)
.join(" ");
.map(([key, value]) => {
if (key === "style") return "";
return typeof value === "boolean" && value ? key : `${key}="${value}"`;
})
.join(" ")
.trim();

const childrenString =
typeof children === "string"
Expand Down Expand Up @@ -97,14 +107,82 @@ export const Playground = ({
(prop: string, control: Control) => {
const propMeta = data?.meta.docgen.props[prop];

if (!propMeta) return null;
// override control type
if (control.type) {
if (propMeta.type.name === "enum") {
if (control.type === "slider") {
const min = 1;
const max = propMeta.type.value.length;

const formattedLabel = (label: React.ReactNode) => {
if (!label) return "";
return propMeta.type.value[
parseInt(label as string, 10) - 1
].value.replace(/"/g, "");
};

return (
<HvSlider
label={prop}
hideInput
minPointValue={min}
maxPointValue={max}
markStep={1}
divisionQuantity={max - min}
classes={{
root: css({ width: "100%" }),
sliderContainer: css({ padding: 10, paddingTop: 0 }),
labelContainer: css({ marginLeft: 0, marginBottom: 8 }),
}}
values={[
propMeta.type.value.findIndex(
(p: any) => p.value.replace(/"/g, "") === propsState[prop],
) + 1 ||
propMeta.type.value.findIndex(
(p: any) =>
p.value.replace(/"/g, "") === control.defaultValue,
) + 1,
]}
formatMark={(label) => formattedLabel(label)}
formatTooltip={(label) => formattedLabel(label)}
onChange={(values) => {
handleSelectChange(
null,
prop,
propMeta.type.value[values[0] - 1]?.value.replace(/"/g, ""),
);
}}
/>
);
}
if (control.type === "radio") {
return (
<HvRadioGroup
label={prop}
orientation="horizontal"
value={propsState[prop] || control.defaultValue}
onChange={(e, v) => handleSelectChange(e, prop, v)}
classes={{
root: css({ width: "100%" }),
}}
>
{propMeta.type.value.map((v: any) => {
const value = v.value.replace('"', "").replace('"', "");
return <HvRadio key={value} label={value} value={value} />;
})}
</HvRadioGroup>
);
}
}
}

// default control types
if (propMeta.type.name === "enum") {
return (
<HvSelect
key={`${prop}`}
value={propsState[prop] || control.defaultValue}
style={{ minWidth: 100, width: "80%" }}
style={{ minWidth: 100, width: "100%" }}
label={prop}
onChange={(e, value) => handleSelectChange(e, prop, value)}
>
Expand Down Expand Up @@ -149,13 +227,13 @@ export const Playground = ({

return (
<>
<div className="flex justify-between p-2 mt-1 border border-[var(--uikit-colors-atmo4)] rounded-t-round">
<div className="w-[70%] flex justify-center items-center">
<div className="flex justify-between mt-1 border border-[var(--uikit-colors-atmo4)] rounded-t-round">
<div className="w-[70%] flex justify-center items-center p-sm">
<Component {...propsState} {...componentProps}>
{children?.props.children}
</Component>
</div>
<div className="w-[30%] flex flex-col gap-[var(--uikit-space-xs)] justify-center items-center border-l border-[var(--uikit-colors-atmo3)] pl-[var(--uikit-space-sm)]">
<div className="w-[30%] flex flex-col gap-xs justify-center items-center border-l border-[var(--uikit-colors-atmo3)] p-sm pl-xs">
{Object.keys(controls || {}).map((prop) => {
const control = controls[prop];
if (!control) return null;
Expand Down
1 change: 1 addition & 0 deletions apps/docs/src/pages/components/_meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export default {
},
avatar: "Avatar",
badge: "Badge",
banner: "Banner",
button: "Button",
};
17 changes: 5 additions & 12 deletions apps/docs/src/pages/components/avatar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,9 @@ import Playground from "./Playground";
import { getComponentData } from "../../utils/utils";

export const getStaticProps = async ({ params }) => {

const meta = await getComponentData("Avatar", "core", avatarClasses)

return {
props: {
ssg: {
meta: meta,
},
},
}

}
const meta = await getComponentData("Avatar", "core", avatarClasses);
return { props: { ssg: { meta: meta } } };
};

<Description />

Expand All @@ -32,9 +23,11 @@ meta: meta,
componentName="HvAvatar"
controls={{
size: {
type: "slider",
defaultValue: "md",
},
variant: {
type: "radio",
defaultValue: "circular",
},
}}
Expand Down
21 changes: 6 additions & 15 deletions apps/docs/src/pages/components/badge.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,15 @@ import { Page } from "@docs/components/Page";
import { getComponentData } from "../../utils/utils";

export const getStaticProps = async ({ params }) => {

const meta = await getComponentData("Badge", "core", badgeClasses)

return {
props: {
ssg: {
meta: meta,
},
},
}

}
const meta = await getComponentData("Badge", "core", badgeClasses);
return { props: { ssg: { meta: meta } } };
};

<Description />

<Page>

### With icon
### Icon

Use the `icon` prop to specify a icon to use with the badge.

Expand All @@ -50,7 +41,7 @@ Use the `maxCount` prop to specify the maximum value to be displayed. Above that
</>
```

### With text
### Text

To display text with the badge you can use the `text` prop along with the `textVariant` prop to specify the typography variant to use.

Expand All @@ -65,7 +56,7 @@ To display text with the badge you can use the `text` prop along with the `textV
</>
```

### With state
### Controlled count

A badge sample using react hooks to set the number of events.

Expand Down
125 changes: 125 additions & 0 deletions apps/docs/src/pages/components/banner.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { css } from "@emotion/css";
import { bannerClasses, HvBanner } from "@hitachivantara/uikit-react-core";

import { Description } from "@docs/components/Description";
import { Page } from "@docs/components/Page";

import Playground from "./Playground";

import { getComponentData } from "../../utils/utils";

export const getStaticProps = async ({ params }) => {
const meta = await getComponentData("Banner", "core", bannerClasses);
return { props: { ssg: { meta: meta } } };
};

<Description />

<Page>

<Playground
Component={HvBanner}
componentName="HvBanner"
controls={{
variant: {
defaultValue: "default",
},
showIcon: {
defaultValue: true,
},
}}
componentProps={{
open: true,
label: "This is an informational message.",
style: { position: "relative", top: 0 },
}}
/>

### Aactions

You can add a set of actions to the banner by specifying the `actions` prop. The `onAction` prop is a callback function that is called when an action is clicked.

```jsx live
<HvBanner
open
offset={0}
label="This is a banner."
showIcon
actions={[
{ id: "action_1", label: "Action 1", disabled: false },
{ id: "action_2", label: "Action 2", disabled: false },
]}
onAction={(event, action) => console.log("Clicked", action)}
style={{ position: "relative", top: 0 }}
/>
```

### Custom icons

You can pass a custom icon to the banner by using the `customIcon` prop.

```jsx live
<HvBanner
open
offset={0}
label="This is a banner with a custom icon."
customIcon={<LightOn color="base_dark" />}
style={{ position: "relative", top: 0 }}
/>
```

### Controlled

Controlled banner. Click the buttons to display different banners.

```jsx live
import { useState } from "react";

export default function Demo() {
const SimpleBanner = ({
variant,
...others
}: Omit<HvBannerProps, "open">) => {
const [open, setOpen] = useState(false);

return (
<>
<HvButton
onClick={() => setOpen(true)}
color="primary"
style={{ width: "150px", textTransform: "capitalize", margin: 10 }}
>
{variant}
</HvButton>
<HvBanner
open={open}
onClose={() => setOpen(false)}
offset={10}
variant={variant}
showIcon
actions={
<HvButton variant="secondaryGhost" style={{ color: "inherit" }}>
Action
</HvButton>
}
bannerContentProps={{
actionProps: { "aria-label": "Close the banner" },
}}
{...others}
/>
</>
);
};

return (
<>
<SimpleBanner variant="default" label="This is a banner." />
<SimpleBanner variant="success" label="This is a success banner." />
<SimpleBanner variant="error" label="This is an error banner." />
</>
);

}
```

</Page>
Loading

0 comments on commit e04aaa1

Please sign in to comment.