Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(FormElement): add docs & refactor component structure #4450

Merged
merged 3 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions apps/docs/src/components/code/Controls.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useData } from "nextra/hooks";
import {
HvCheckBox,
HvColorPicker,
Expand All @@ -14,7 +15,7 @@ type OptionType = {
value: string;
};

type ControlType = "radio" | "check" | "slider" | "color" | "text";
type ControlType = "check" | "color" | "radio" | "select" | "slider" | "text";

export type Control = {
type?: ControlType;
Expand All @@ -26,19 +27,13 @@ type ControlsProps = {
prop: string;
state: Record<string, any>;
control: Control;
data: Record<string, any>;
onChange: (prop: string, value: unknown) => void;
};

export const Controls = ({
prop,
state,
control,
data,
onChange,
}: ControlsProps) => {
export const Controls = ({ prop, state, control, onChange }: ControlsProps) => {
const { colors } = useTheme();
const propMeta = data?.meta.docgen.props[prop];
const { meta } = useData();
const propMeta = meta.docgen.props[prop];
const type = control?.type || propMeta?.type?.name || "text";

// Utility to clean up values (e.g., remove quotes)
Expand Down Expand Up @@ -176,7 +171,7 @@ export const Controls = ({
return renderControl();
}

// @eslint-disable-next-line no-console
// eslint-disable-next-line no-console
console.error(`Control for "${prop}" not supported: ${type}`);
return null;
};
3 changes: 0 additions & 3 deletions apps/docs/src/components/code/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { isValidElement, useCallback, useState } from "react";
import jsxToString from "react-element-to-jsx-string";
import { CodeEditor } from "react-live-runner";
import useEditorTheme from "@docs/hooks/useEditorTheme";
import { useData } from "nextra/hooks";

import { Controls, type Control } from "./Controls";

Expand Down Expand Up @@ -55,7 +54,6 @@ const Playground = ({
controls = {},
children,
}: PlaygroundProps) => {
const controlData = useData();
const editorTheme = useEditorTheme();

// Initialize dynamic props with default values from controls
Expand Down Expand Up @@ -109,7 +107,6 @@ const Playground = ({
prop={prop}
state={dynamicProps}
control={control}
data={controlData}
onChange={updatePropValue}
/>
);
Expand Down
1 change: 1 addition & 0 deletions apps/docs/src/components/code/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const Toolbar = ({
setCopySuccess(true);
setTimeout(() => setCopySuccess(false), 2000); // Reset icon after 2 seconds
} catch (error) {
// eslint-disable-next-line no-console
console.error("Failed to copy code:", error);
}
};
Expand Down
8 changes: 4 additions & 4 deletions apps/docs/src/components/page/Classes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ export const Classes = () => {
className="table-row border-atmo3 !bg-transparent"
>
<HvTableCell className="w-[25%] !pl-1">
<HvTypography>{c.name}</HvTypography>
<code>{c.name}</code>
</HvTableCell>
<HvTableCell>
<HvTypography className="!text-primary_80 !text-[12px]">
<pre>.{c.selector}</pre>
</HvTypography>
<pre className="!text-primary_80 !text-[12px]">
{`.${c.selector}`}
</pre>
</HvTableCell>
</HvTableRow>
))}
Expand Down
36 changes: 15 additions & 21 deletions apps/docs/src/components/page/Props.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,31 +78,25 @@ const PropsTable = ({ propsObj }: PropsTableProps): JSX.Element => (
) : (
Object.entries(propsObj).map(([key, propItem]) => (
<HvTableRow key={key} className={classes.row}>
<HvTableCell className="!pl-1 w-[25%]">
<HvTypography>
{key}
{propItem.required && "*"}
</HvTypography>
<HvTableCell className="!pl-xs w-[25%]">
<code>{key}</code>
{propItem.required && <code className="text-negative">*</code>}
</HvTableCell>
<HvTableCell className="w-[30%]">
<HvTypography className="!text-[12px] !text-primary_80">
<pre className="whitespace-pre-wrap break-words">
{propItem.type?.name}
</pre>
</HvTypography>
<pre className="whitespace-pre-wrap break-words !text-[12px] text-primary_80">
{propItem.type?.name}
</pre>
</HvTableCell>
<HvTableCell>
<HvTypography>
{propItem.description
?.split(/(`[^`]+`)/g)
.map((part) =>
part.startsWith("`") && part.endsWith("`") ? (
<code key={part}>{part.slice(1, -1)}</code>
) : (
part
),
)}
</HvTypography>
{propItem.description?.split(/(`[^`]+`)/g).map((part) =>
part.startsWith("`") && part.endsWith("`") ? (
<code key={part} className="nextra-code">
{part.slice(1, -1)}
</code>
) : (
part
),
)}
</HvTableCell>
</HvTableRow>
))
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/pages/components/accordion.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const getStaticProps = async ({ params }) => {

### Controlled

You can control the state of `HvAccordion` by using the `expanded / onChange` props.
You can control the state of `HvAccordion` by using the `expanded`/`onChange` props.

```jsx live
import { useState } from "react";
Expand Down
137 changes: 12 additions & 125 deletions apps/docs/src/pages/components/checkbox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,58 +46,14 @@ export const getStaticProps = async ({ params }) => {
### States

```jsx live
<HvStack>
<HvTypography variant="title3">Disabled</HvTypography>
<HvStack direction="row">
<HvCheckBox disabled label="Checkbox 1" />
<HvCheckBox defaultChecked disabled label="Checkbox 2" />
<HvCheckBox indeterminate disabled label="Checkbox 3" />
</HvStack>
<HvTypography variant="title3">Readonly</HvTypography>
<HvStack direction="row">
<HvCheckBox readOnly label="Checkbox 1" />
<HvCheckBox defaultChecked readOnly label="Checkbox 2" />
<HvCheckBox indeterminate readOnly label="Checkbox 3" />
</HvStack>
<HvTypography variant="title3">Required</HvTypography>
<HvStack direction="row">
<HvCheckBox required label="Checkbox 1" />
<HvCheckBox required defaultChecked label="Checkbox 2" />
<HvCheckBox required indeterminate label="Checkbox 3" />
</HvStack>
<HvTypography variant="title3">Invalid</HvTypography>
<HvStack direction="row">
<HvCheckBox
status="invalid"
statusMessage="No way for this to be valid!"
label="Checkbox 1"
/>
<HvCheckBox
status="invalid"
statusMessage="No way for this to be valid!"
defaultChecked
label="Checkbox 2"
/>
<HvCheckBox
status="invalid"
statusMessage="No way for this to be valid!"
indeterminate
label="Checkbox 3"
/>
</HvStack>
<HvTypography variant="title3">No label</HvTypography>
<HvStack direction="row">
<HvCheckBox aria-label="Checkbox 1" />
<HvCheckBox defaultChecked aria-label="Checkbox 2" />
<HvCheckBox indeterminate aria-label="Checkbox 3" />
</HvStack>
<HvTypography variant="title3">Semantic</HvTypography>
<HvStack direction="row">
<HvCheckBox semantic aria-label="Checkbox 1" />
<HvCheckBox semantic defaultChecked aria-label="Checkbox 2" />
<HvCheckBox semantic indeterminate aria-label="Checkbox 3" />
</HvStack>
</HvStack>
<div className="flex gap-xs items-start">
<HvCheckBox label="Checkbox" />
<HvCheckBox required label="Required" />
<HvCheckBox defaultChecked label="Checked" />
<HvCheckBox checked indeterminate label="Partial" />
<HvCheckBox status="invalid" statusMessage="This is invalid!" label="Error" />
<HvCheckBox aria-label="No visible label" />
</div>
```

### Controlled
Expand All @@ -123,79 +79,10 @@ export default function Demo() {
}
```

### External error message
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a awkward example to add for each form component. it should be added in a complete form example/template instead

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a callout on each form component documentation page to direct people to the FormElement page (not just on the related components section)

### Related components

A form element can be invalid but render its error message elsewhere. For instance if a business rule error relates to the combination of two or more fields, or if we want to display all the form errors together in a summary section. The aria-errormessage property should reference another element that contains error message text. It can be used when controlling the validation status or when relying on the built-in validations, but the message text computation is reponsability of the app.

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

export default function Demo() {
// prettier-ignore
const [firstCheckboxErrorMessage, setFirstCheckboxErrorMessage] = useState<string | null>(null);
// prettier-ignore
const [secondCheckboxErrorMessage, setSecondCheckboxErrorMessage] = useState<string | null>("No way for the second checkbox to be valid!");

return (
<HvGrid container>
<HvGrid container item xs={12} md={6}>
<HvGrid item xs={12}>
<HvCheckBox
required
defaultChecked
aria-errormessage="firstCheckbox-error"
onChange={(_, checked) => {
if (checked) {
setFirstCheckboxErrorMessage(null);
} else if (!checked) {
setFirstCheckboxErrorMessage(
"You must check the first checkbox",
);
}
}}
label="First Checkbox"
/>
</HvGrid>
<HvGrid item xs={12}>
<HvCheckBox
status="invalid"
aria-errormessage="secondCheckbox-error"
onChange={() => {
setSecondCheckboxErrorMessage(
"No way for the second checkbox to be valid! I told you!",
);
}}
label="Second Checkbox"
/>
</HvGrid>
</HvGrid>

<HvGrid item xs={12} md={6}>
<div className="bg-negative_20 text-base_dark p-md">
<HvTypography
component="h4"
variant="title4"
className="text-inherit"
>
Form errors:
</HvTypography>
<ul className="my-sm pl-md">
{firstCheckboxErrorMessage && (
<li id="firstCheckbox-error" aria-live="polite">
{firstCheckboxErrorMessage}
</li>
)}
{secondCheckboxErrorMessage && (
<li id="secondCheckbox-error" aria-live="polite">
{secondCheckboxErrorMessage}
</li>
)}
</ul>
</div>
</HvGrid>
</HvGrid>
);
}
```
- [`HvFormElement`](/components/form-element)
- [`HvInput`](/components/input)
- [`HvRadio`](/components/radio)

</Page>
1 change: 1 addition & 0 deletions apps/docs/src/pages/components/date-picker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export default function Demo() {

### Related components

- [`HvFormElement`](/components/form-element)
- [`HvInput`](/components/input)
- [`HvTimePicker`](/components/time-picker)

Expand Down
Loading