Skip to content

Commit

Permalink
Update the checkbox control to also show radio buttons depending on i…
Browse files Browse the repository at this point in the history
…f default is used
  • Loading branch information
janechu committed Jan 5, 2024
1 parent e0a6a59 commit 8256dab
Show file tree
Hide file tree
Showing 24 changed files with 202 additions and 77 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ test.describe("checkbox", () => {
test("blur", async ({ page }) => {
await page.goto("/form?schema=controlCheckboxInvalid");
await page.waitForSelector("input");
await page.locator("input").focus();
await page.locator("input").blur();
const radio = await page.locator("input[type='radio']").nth(0);
await radio.focus();
await radio.blur();
await expect(page).toHaveScreenshot();
});
test("inline", async ({ page }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ test.describe("CheckboxControl", () => {
test("should generate an HTML input element with type 'checkbox'", async ({
page,
}) => {
await page.goto("/form?schema=controlCheckbox");
await page.goto("/form?schema=controlCheckboxDefault");

await page.waitForSelector("input");

Expand All @@ -14,14 +14,23 @@ test.describe("CheckboxControl", () => {

await expect(await checkbox.getAttribute("type")).toEqual("checkbox");
});
test("should generate HTML input elements with type 'radio'", async ({ page }) => {
await page.goto("/form?schema=controlCheckbox");

await page.waitForSelector("input");

const checkbox = await page.locator("input[type='radio']");

await expect(await checkbox.count()).toEqual(2);
});
test("should be disabled when disabled props is passed", async ({ page }) => {
await page.goto("/form?schema=controlCheckboxDisabled");

await page.waitForSelector("input");

const textarea = await page.locator("input");
const radio = await page.locator("input").nth(0);

await expect(await textarea.getAttribute("disabled")).toEqual("");
await expect(await radio.getAttribute("disabled")).toEqual("");
});
test("should have the default class when default prop is passed", async ({
page,
Expand All @@ -45,9 +54,9 @@ test.describe("CheckboxControl", () => {

await page.waitForSelector("input");

const checkbox = await page.locator("input");
const radio = await page.locator("input").nth(0);

await checkbox.click();
await radio.click();

const pre = await page.locator("pre");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,46 @@
width: 1px;
background: var(--dtc-text-color);
}

.dtc-radio-control {
display: flex;
column-gap: 8px;
}

.dtc-radio-control label {
display: flex;
align-items: center;
grid-template-columns: 12px auto;
gap: 4px;
}

.dtc-radio-control input {
/* Add if not using autoprefixer */
-webkit-appearance: none;
appearance: none;
/* For iOS < 15 to remove gradient background */
background-color: var(--dtc-l3-fill-color);
/* Not removed via appearance */
margin: 0;
font: inherit;
color: currentColor;
width: 12px;
height: 12px;
border-radius: 50%;
display: grid;
place-content: center;
}

.dtc-radio-control input::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
transform: scale(0);
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em var(--dtc-text-color);
}

.dtc-radio-control input:checked::before {
transform: scale(1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from "react";
import { CheckboxControlProps } from "./control.checkbox-or-radios.props";
import { classNames } from "@microsoft/fast-web-utilities";
import { isDefault } from "../utilities/form";
import cssVariables from "design-to-code/dist/stylesheets/web-components/style/global.css-variables.css";
import style from "./control.checkbox-or-radios.style.css";
import { uniqueId } from "lodash-es";

// tree-shaking
cssVariables;
style;

const trueRadioId = `dtc-${uniqueId()}`;
const falseRadioId = `dtc-${uniqueId()}`;

/**
* Form control definition
*/
function CheckboxOrRadiosControl(props: CheckboxControlProps) {
const containsDefault: boolean = typeof props.default === "boolean";
const value: boolean =
typeof props.value === "boolean"
? props.value
: containsDefault
? props.default
: false;

function handleChange(): (e: React.ChangeEvent<HTMLInputElement>) => void {
return (e: React.ChangeEvent<HTMLInputElement>): void => {
props.onChange({
value:
e.target.type === "checkbox"
? e.target.checked
: e.target.value === "false"
? false
: true,
});
};
}

function renderCheckbox() {
return (
<div
className={classNames(
"dtc-checkbox-control",
["dtc-checkbox-control__disabled", props.disabled],
[
"dtc-checkbox-control__default",
isDefault(props.value, props.default),
]
)}
>
<input
className={"dtc-checkbox-control_input"}
id={props.dataLocation}
type={"checkbox"}
value={value.toString()}
onChange={handleChange()}
checked={value}
disabled={props.disabled}
ref={props.elementRef as React.Ref<HTMLInputElement>}
onFocus={props.reportValidity}
onBlur={props.updateValidity}
/>
<span className={"dtc-checkbox-control_checkmark"} />
</div>
);
}

function renderRadios() {
return (
<div
className={classNames(
"dtc-radio-control",
["dtc-radio-control__disabled", props.disabled],
["dtc-radio-control__default", isDefault(props.value, props.default)]
)}
>
<label htmlFor={`${props.dataLocation}${trueRadioId}`}>
<input
className={"dtc-radio-control_input"}
id={`${props.dataLocation}${trueRadioId}`}
type={"radio"}
name={props.dataLocation}
value={"true"}
onChange={handleChange()}
checked={value.toString() === "true"}
ref={props.elementRef as React.Ref<HTMLInputElement>}
onFocus={props.reportValidity}
onBlur={props.updateValidity}
disabled={props.disabled}
/>
True
</label>
<label htmlFor={`${props.dataLocation}${falseRadioId}`}>
<input
className={"dtc-radio-control_input"}
id={`${props.dataLocation}${falseRadioId}`}
type={"radio"}
name={props.dataLocation}
value={"false"}
onChange={handleChange()}
checked={value.toString() === "false"}
ref={props.elementRef as React.Ref<HTMLInputElement>}
onFocus={props.reportValidity}
onBlur={props.updateValidity}
disabled={props.disabled}
/>
False
</label>
</div>
);
}

// An assumption is made that if a default is present,
// the only reason to change the value is to set the opposite value
if (containsDefault) {
return renderCheckbox();
}

return renderRadios();
}

export { CheckboxOrRadiosControl };
export default CheckboxOrRadiosControl;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ArrayControl from "./control.array";
import ButtonControl from "./control.button";
import CheckboxControl from "./control.checkbox";
import CheckboxControl from "./control.checkbox-or-radios";
import DisplayControl from "./control.display";
import NumberFieldControl from "./control.number-field";
import SectionControl from "./control.section";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ function ControlSwitch(props: ControlSwitchProps) {
return renderButton(
control !== undefined ? control : props.controls.button
);
case ControlType.checkbox:
return renderCheckbox(
case ControlType.checkboxOrRadios:
return renderCheckboxOrRadios(
control !== undefined ? control : props.controls.checkbox
);
case ControlType.display:
Expand Down Expand Up @@ -91,13 +91,15 @@ function ControlSwitch(props: ControlSwitchProps) {
case ControlType.date:
return renderDate(control !== undefined ? control : props.controls.date);
case ControlType.time:
return renderDate(control !== undefined ? control : props.controls.time);
return renderTime(control !== undefined ? control : props.controls.time);
case ControlType.dateTime:
return renderDate(
return renderDateTime(
control !== undefined ? control : props.controls.dateTime
);
case ControlType.email:
return renderDate(control !== undefined ? control : props.controls.email);
return renderEmail(
control !== undefined ? control : props.controls.email
);
}

return null;
Expand Down Expand Up @@ -135,7 +137,7 @@ function ControlSwitch(props: ControlSwitchProps) {

switch (props.schema.type) {
case "boolean":
return ControlType.checkbox;
return ControlType.checkboxOrRadios;
case "number":
return ControlType.numberField;
case "string":
Expand Down Expand Up @@ -186,10 +188,10 @@ function ControlSwitch(props: ControlSwitchProps) {
}

/**
* Renders the checkbox form item
* Renders the checkbox/radios form item
*/
function renderCheckbox(control: SingleLineControlPlugin): React.ReactNode {
control.updateProps(getCommonControlProps(ControlType.checkbox));
function renderCheckboxOrRadios(control: SingleLineControlPlugin): React.ReactNode {
control.updateProps(getCommonControlProps(ControlType.checkboxOrRadios));

return control.render();
}
Expand Down Expand Up @@ -344,7 +346,7 @@ function ControlSwitch(props: ControlSwitchProps) {
function shouldBeSoftRemovable(type: ControlType): boolean {
return ![
ControlType.button,
ControlType.checkbox,
ControlType.checkboxOrRadios,
ControlType.display,
ControlType.select,
].includes(type);
Expand Down
6 changes: 3 additions & 3 deletions packages/design-to-code-react/src/form/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,9 @@ const Form: React.FC<FormProps> = (
component: NumberFieldControl,
context: ControlContext.fill,
};
case ControlType.checkbox:
case ControlType.checkboxOrRadios:
return {
plugin: SingleLineControlPlugin,
plugin: StandardControlPlugin,
component: CheckboxControl,
context: ControlContext.default,
};
Expand Down Expand Up @@ -341,7 +341,7 @@ const Form: React.FC<FormProps> = (
);
checkboxControl = findControlPlugin(
hasCustomControlPlugins,
ControlType.checkbox
ControlType.checkboxOrRadios
);
sectionLinkControl = findControlPlugin(
hasCustomControlPlugins,
Expand Down
2 changes: 1 addition & 1 deletion packages/design-to-code-react/src/form/templates/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export enum ControlType {
select = "select",
array = "array",
linkedData = "linkedData",
checkbox = "checkbox",
checkboxOrRadios = "checkboxOrRadios",
numberField = "numberField",
section = "section",
sectionLink = "sectionLink",
Expand Down

0 comments on commit 8256dab

Please sign in to comment.