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

[WB-1814.1] Refactor Checkbox and Radio to use semantic colors #2439

Merged
merged 13 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 6 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
5 changes: 5 additions & 0 deletions .changeset/unlucky-planes-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/wonder-blocks-form": patch
---

Migrate Radio and Checkbox to use semanticColor tokens
92 changes: 92 additions & 0 deletions __docs__/components/all-variants.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as React from "react";
import type {StrictArgs} from "@storybook/react";

import {StyleSheet} from "aphrodite";
import {addStyle} from "@khanacademy/wonder-blocks-core";
import {
border,
semanticColor,
spacing,
} from "@khanacademy/wonder-blocks-tokens";

import {LabelLarge} from "@khanacademy/wonder-blocks-typography";

const StyledTable = addStyle("table");
const StyledTh = addStyle("th");
const StyledTd = addStyle("td");

type Variant = {name: string; props: StrictArgs};

type Props = {
/**
* The children as a function that receives the state props used to render
* each variant of the component.
*/
children: (props: any) => React.ReactNode;
/**
* The categories to display in the table as columns.
*/
rows: Array<Variant>;
/**
* The states to display in the table as rows.
*/
columns: Array<Variant>;
};

/**
* A table that displays all possible variants of a component.
*/
export function AllVariants({children, columns, rows}: Props) {
return (
<StyledTable style={styles.table}>
<thead>
<tr>
<StyledTh style={styles.cell}>
<LabelLarge>Category / State</LabelLarge>
</StyledTh>
{columns.map((col, index) => (
<StyledTh key={index} scope="col" style={styles.cell}>
<LabelLarge>{col.name}</LabelLarge>
</StyledTh>
))}
</tr>
</thead>
<tbody>
{rows.map((row, idx) => (
<tr key={idx}>
<StyledTd scope="row" style={styles.cell}>
<LabelLarge>{row.name}</LabelLarge>
</StyledTd>
{columns.map((col) => (
<StyledTd
key={col.name}
style={[
styles.cell,
{
border: `${border.width.hairline}px dashed ${semanticColor.border.primary}`,
},
]}
>
{children({
...row.props,
...col.props,
})}
</StyledTd>
))}
</tr>
))}
</tbody>
</StyledTable>
);
}

const styles = StyleSheet.create({
table: {
borderCollapse: "collapse",
textAlign: "left",
},
cell: {
margin: spacing.medium_16,
padding: spacing.medium_16,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@ import {StyleSheet} from "aphrodite";
import {PropsFor, View} from "@khanacademy/wonder-blocks-core";
import {Checkbox} from "@khanacademy/wonder-blocks-form";
import {LabelSmall} from "@khanacademy/wonder-blocks-typography";
import {color} from "@khanacademy/wonder-blocks-tokens";
import {semanticColor} from "@khanacademy/wonder-blocks-tokens";

type CheckboxProps = PropsFor<typeof Checkbox>;

const ErrorTemplate = (args: CheckboxProps) => {
const [checked, setChecked] = React.useState(false);
const errorId = React.useId();
jandrade marked this conversation as resolved.
Show resolved Hide resolved
const errorState = !checked;
return (
<View>
<Checkbox
error={errorState}
aria-describedby={errorState ? "error-message" : undefined}
aria-describedby={errorState ? errorId : undefined}
aria-required={true}
{...args}
checked={checked}
onChange={setChecked}
/>
{errorState && (
<LabelSmall style={styles.error} id="error-message">
<LabelSmall style={styles.error} id={errorId}>
You must agree to the terms to continue
</LabelSmall>
)}
Expand All @@ -45,7 +46,7 @@ const DisabledTemplate = (args: CheckboxProps) => {

const styles = StyleSheet.create({
error: {
color: color.red,
color: semanticColor.status.critical.foreground,
},
});

Expand Down
6 changes: 3 additions & 3 deletions __docs__/wonder-blocks-form/checkbox-group.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {StyleSheet} from "aphrodite";
import type {Meta, StoryObj} from "@storybook/react";

import {View} from "@khanacademy/wonder-blocks-core";
import {color, spacing} from "@khanacademy/wonder-blocks-tokens";
import {semanticColor, spacing} from "@khanacademy/wonder-blocks-tokens";
import {LabelLarge, LabelXSmall} from "@khanacademy/wonder-blocks-typography";

import {Choice, CheckboxGroup} from "@khanacademy/wonder-blocks-form";
Expand Down Expand Up @@ -329,7 +329,7 @@ const styles = StyleSheet.create({
},
title: {
paddingBottom: spacing.xSmall_8,
borderBottom: `1px solid ${color.offBlack64}`,
borderBottom: `1px solid ${semanticColor.border.strong}`,
},
// Multiple choice styling
multipleChoice: {
Expand All @@ -339,7 +339,7 @@ const styles = StyleSheet.create({
justifyContent: "center",
},
description: {
color: color.offBlack64,
color: semanticColor.text.secondary,
},
last: {
borderBottom: "solid 1px #CCC",
Expand Down
69 changes: 69 additions & 0 deletions __docs__/wonder-blocks-form/checkbox-variants.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from "react";
import type {Meta, StoryObj} from "@storybook/react";

import {Checkbox} from "@khanacademy/wonder-blocks-form";

import {AllVariants} from "../components/all-variants";

const rows = [
{name: "Unchecked", props: {checked: false}},
{name: "Checked", props: {checked: true}},
{name: "Indeterminate", props: {checked: null}},
];

const columns = [
{
name: "Default",
props: {},
},
{
name: "Disabled",
props: {disabled: true},
},
{
name: "Error",
props: {error: true},
},
];

type Story = StoryObj<typeof Checkbox>;

/**
* The following stories are used to generate the pseudo states for the Checkbox
* component. This is only used for visual testing in Chromatic.
*/
const meta = {
title: "Packages / Form / Checkbox / Checkbox - All Variants",
component: Checkbox,
render: (args) => (
<AllVariants rows={rows} columns={columns}>
{(props) => <Checkbox {...args} {...props} />}
</AllVariants>
),
args: {
label: "Label",
description: "Description",
},
tags: ["!autodocs"],
} satisfies Meta<typeof Checkbox>;

export default meta;

export const Default: Story = {};

export const Hover: Story = {
parameters: {pseudo: {hover: true}},
};

export const Focus: Story = {
parameters: {pseudo: {focusVisible: true}},
};

export const HoverFocus: Story = {
name: "Hover + Focus",
parameters: {pseudo: {hover: true, focusVisible: true}},
};

export const Active: Story = {
parameters: {pseudo: {active: true}},
};
22 changes: 4 additions & 18 deletions __docs__/wonder-blocks-form/checkbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export default {
version={packageConfig.version}
/>
),
chromatic: {
// These stories are being tested in checkbox-variants.stories.tsx
disableSnapshot: true,
},
},
} as Meta<typeof Checkbox>;

Expand All @@ -33,14 +37,6 @@ export const Default: StoryComponentType = {
},
};

Default.parameters = {
chromatic: {
// We already have screenshots of another story that covers
// this and more cases.
disableSnapshot: true,
},
};

export const Controlled: StoryComponentType = () => {
const [checked, setChecked] = React.useState<boolean | null>(null);

Expand All @@ -55,11 +51,6 @@ export const Controlled: StoryComponentType = () => {
};

Controlled.parameters = {
chromatic: {
// Disabling because this doesn't test visuals, its for testing
// that `state` works as expected.
disableSnapshot: true,
},
docs: {
description: {
story: `Use state to keep track of whether the checkbox
Expand Down Expand Up @@ -258,11 +249,6 @@ export const VariantsControlled: StoryComponentType = () => {
};

VariantsControlled.parameters = {
chromatic: {
// Disabling because this doesn't test visuals, its for testing
// that `state` works as expected.
disableSnapshot: true,
},
docs: {
description: {
story: `A demo of the different kinds of checkboxes
Expand Down
70 changes: 70 additions & 0 deletions __docs__/wonder-blocks-form/radio-variants.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from "react";
import type {Meta, StoryObj} from "@storybook/react";

// NOTE: Radio is an internal component and should not be used directly. Use
// RadioGroup instead. This import is only used for visual testing in Chromatic.
import Radio from "../../packages/wonder-blocks-form/src/components/radio";

import {AllVariants} from "../components/all-variants";

const rows = [
{name: "Unchecked", props: {checked: false}},
{name: "Checked", props: {checked: true}},
];

const columns = [
{
name: "Default",
props: {},
},
{
name: "Disabled",
props: {disabled: true},
},
{
name: "Error",
props: {error: true},
},
];

type Story = StoryObj<typeof Radio>;

/**
* The following stories are used to generate the pseudo states for the Radio
* component. This is only used for visual testing in Chromatic.
*/
const meta = {
title: "Packages / Form / Radio (internal) / Radio - All Variants",
component: Radio,
render: (args) => (
<AllVariants rows={rows} columns={columns}>
{(props) => <Radio {...args} {...props} />}
</AllVariants>
),
args: {
label: "Label",
description: "Description",
},
tags: ["!autodocs"],
} satisfies Meta<typeof Radio>;

export default meta;

export const Default: Story = {};

export const Hover: Story = {
parameters: {pseudo: {hover: true}},
};

export const Focus: Story = {
parameters: {pseudo: {focusVisible: true}},
};

export const HoverFocus: Story = {
name: "Hover + Focus",
parameters: {pseudo: {hover: true, focusVisible: true}},
};

export const Active: Story = {
parameters: {pseudo: {active: true}},
};
17 changes: 4 additions & 13 deletions __docs__/wonder-blocks-form/radio.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export default {
version={packageConfig.version}
/>
),
chromatic: {
// These stories are being tested in radio-variants.stories.tsx
disableSnapshot: true,
},
},
} as Meta<typeof Radio>;

Expand All @@ -32,25 +36,12 @@ export const Default: StoryComponentType = {
},
};

Default.parameters = {
chromatic: {
// We already have screenshots of another story that covers
// this and more cases.
disableSnapshot: true,
},
};

export const Controlled: StoryComponentType = () => {
const [checked, setChecked] = React.useState(false);
return <Radio checked={checked} onChange={setChecked} />;
};

Controlled.parameters = {
chromatic: {
// Disabling because this doesn't test visuals, it tests
// that the `checked` state works as expected.
disableSnapshot: true,
},
docs: {
description: {
story: `Use state to keep track of whether
Expand Down
Loading
Loading