Skip to content

Commit

Permalink
fix(@rjsf/chakra-ui) Unify appearance of chakra-ui SelectWidget for s…
Browse files Browse the repository at this point in the history
…ingle and multiple-choice (rjsf-team#3182)

* @rjsf/chakra-ui: support chakra-react-select v4;
In particular move  from  to  and add  to peerDependencies

* Correct rjsf version

* Add chakra-react-select to pre-requisites in chakra-ui README

* Use Select widget from chakra-react-select for both single- and multi-select;
Display label rather then value for selected items in multi-select

* Update test snapshot

* Remove duplicate test, add new tests

* Update CHANGELOG

* Make it prettier

* Correctly pass uischema option enumDisabled to chakra-react-select widgets

* Add tests for ui:enumDisabled

* Make tests prettier

* Add onFocus and onBlur handlers from wrapping form control

* Add back the autoFocus prop

* Pass a value to single-select widget; Refactor chakra-ui SelectWidget code as suggested in PR review; Update snapshots for tests

* Run cs-format

* chakra-ui SelectWidget: Fix unit tests for isDisabled; Add unit tests for formData

Co-authored-by: dorofel <lisa.dorofeeva@lightsource.ca>
  • Loading branch information
ldorofeeva and dorofel committed Oct 9, 2022
1 parent 6666b18 commit cb27360
Show file tree
Hide file tree
Showing 4 changed files with 2,407 additions and 286 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -24,7 +24,9 @@ should change the heading of the (upcoming) version to include a major version b
- Make label generation consistent with other themes by refactoring the code into the `FieldTemplate` instead of having the widgets implementing the label, fixing [#2007](https://github.com/rjsf-team/react-jsonschema-form/issues/2007)

## @rjsf/chakra-ui
- Added support for `chakra-react-select` v4, fixing [#3152](https://github.com/rjsf-team/react-jsonschema-form/issues/3152).
- Added support for `chakra-react-select` v4, fixing [#3152](https://github.com/rjsf-team/react-jsonschema-form/issues/3152)
- In `SelectWidget` use `Select` from `chakra-react-select` for both single- and multiple-choice select
- In `SelectWidget` multiple-choice select display label rather than value for selected items

## @rjsf/core
- Extended `Form.onChange` to optionally return the `id` of the field that caused the change, fixing [#2768](https://github.com/rjsf-team/react-jsonschema-form/issues/2768)
Expand Down
104 changes: 43 additions & 61 deletions packages/chakra-ui/src/SelectWidget/SelectWidget.tsx
@@ -1,12 +1,8 @@
import React from "react";
import { FormControl, FormLabel, Select } from "@chakra-ui/react";
import { FormControl, FormLabel } from "@chakra-ui/react";
import { processSelectValue, WidgetProps } from "@rjsf/utils";
import { getChakra } from "../utils";
import {
GroupBase,
OptionsOrGroups,
Select as ChakraMultiSelect,
} from "chakra-react-select";
import { GroupBase, OptionsOrGroups, Select } from "chakra-react-select";

const SelectWidget = (props: WidgetProps) => {
const {
Expand All @@ -30,10 +26,6 @@ const SelectWidget = (props: WidgetProps) => {
const { enumOptions, enumDisabled } = options;
const chakraProps = getChakra({ uiSchema });

// TODO: Default emptyValue should be string when multi select is implemented
// const emptyValue = multiple ? [] : "";
const emptyValue = "";

const _onMultiChange = (e: any) => {
return onChange(
processSelectValue(
Expand All @@ -46,19 +38,38 @@ const SelectWidget = (props: WidgetProps) => {
);
};

const _onChange = ({
target: { value },
}: React.ChangeEvent<{ name?: string; value: unknown }>) =>
onChange(processSelectValue(schema, value, options));
const _onBlur = ({
target: { value },
}: React.FocusEvent<HTMLSelectElement>) =>
const _onChange = (e: any) => {
return onChange(processSelectValue(schema, e.value, options));
};

const _onBlur = ({ target: { value } }: React.FocusEvent<HTMLInputElement>) =>
onBlur(id, processSelectValue(schema, value, options));

const _onFocus = ({
target: { value },
}: React.FocusEvent<HTMLSelectElement>) =>
}: React.FocusEvent<HTMLInputElement>) =>
onFocus(id, processSelectValue(schema, value, options));

const _valueLabelMap: any = {};
(enumOptions as any).map((option: any) => {
const { value, label }: any = option;
_valueLabelMap[value] = label;
option["isDisabled"] =
enumDisabled && (enumDisabled as any).indexOf(value) != -1;
});

const isMultiple = typeof multiple !== "undefined" && Boolean(enumOptions);
const formValue: any = isMultiple
? (value || []).map((v: any) => {
return {
label: _valueLabelMap[v] || v,
value: v,
};
})
: {
label: _valueLabelMap[value] || value || "",
value: value || "",
};
return (
<FormControl
mb={1}
Expand All @@ -69,52 +80,23 @@ const SelectWidget = (props: WidgetProps) => {
isInvalid={rawErrors && rawErrors.length > 0}
>
{(label || schema.title) && (
<FormLabel
htmlFor={
typeof multiple !== "undefined" && enumOptions ? undefined : id
}
>
<FormLabel htmlFor={isMultiple ? undefined : id}>
{label || schema.title}
</FormLabel>
)}
{typeof multiple !== "undefined" && enumOptions ? (
<ChakraMultiSelect
inputId={id}
name={id}
isMulti
options={enumOptions as OptionsOrGroups<unknown, GroupBase<unknown>>}
placeholder={placeholder}
closeMenuOnSelect={false}
onChange={_onMultiChange}
value={value.map((v: any) => {
return {
label: v,
value: v,
};
})}
/>
) : (
<Select
id={id}
name={id}
placeholder={placeholder !== "" ? placeholder : " "}
value={typeof value === "undefined" ? emptyValue : value.toString()}
autoFocus={autofocus}
onBlur={_onBlur}
onChange={_onChange}
onFocus={_onFocus}
>
{(enumOptions as any).map(({ value, label }: any, i: number) => {
const disabled: any =
enumDisabled && (enumDisabled as any).indexOf(value) != -1;
return (
<option key={i} value={value} disabled={disabled}>
{label}
</option>
);
})}
</Select>
)}
<Select
inputId={id}
name={id}
isMulti={isMultiple}
options={enumOptions as OptionsOrGroups<unknown, GroupBase<unknown>>}
placeholder={placeholder}
closeMenuOnSelect={false}
onBlur={_onBlur}
onChange={isMultiple ? _onMultiChange : _onChange}
onFocus={_onFocus}
autoFocus={autofocus}
value={formValue}
/>
</FormControl>
);
};
Expand Down
100 changes: 98 additions & 2 deletions packages/chakra-ui/test/Form.test.tsx
Expand Up @@ -183,15 +183,111 @@ describe("single fields", () => {
.toJSON();
expect(tree).toMatchSnapshot();
});
test("checkbox field", () => {
test("select field multiple choice", () => {
const schema: RJSFSchema = {
type: "boolean",
type: "array",
items: {
type: "string",
enum: ["foo", "bar", "fuzz", "qux"],
},
uniqueItems: true,
};
const tree = renderer
.create(<Form schema={schema} validator={validator} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
test("select field multiple choice with labels", () => {
const schema: RJSFSchema = {
type: "array",
items: {
type: "number",
anyOf: [
{
enum: [1],
title: "Blue",
},
{
enum: [2],
title: "Red",
},
{
enum: [3],
title: "Green",
},
],
},
uniqueItems: true,
};
const tree = renderer
.create(<Form schema={schema} validator={validator} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
test("select field single choice enumDisabled", () => {
const schema: RJSFSchema = {
type: "string",
enum: ["foo", "bar"],
};
const uiSchema = {
"ui:enumDisabled": ["bar"],
};
const tree = renderer
.create(
<Form schema={schema} uiSchema={uiSchema} validator={validator} />
)
.toJSON();
expect(tree).toMatchSnapshot();
});
test("select field multiple choice enumDisabled", () => {
const schema: RJSFSchema = {
type: "array",
items: {
type: "string",
enum: ["foo", "bar", "fuzz", "qux"],
},
uniqueItems: true,
};
const uiSchema = {
"ui:enumDisabled": ["bar"],
};
const tree = renderer
.create(
<Form schema={schema} uiSchema={uiSchema} validator={validator} />
)
.toJSON();
expect(tree).toMatchSnapshot();
});
test("select field single choice formData", () => {
const schema: RJSFSchema = {
type: "string",
enum: ["foo", "bar"],
};
const formData = "bar";
const tree = renderer
.create(
<Form schema={schema} formData={formData} validator={validator} />
)
.toJSON();
expect(tree).toMatchSnapshot();
});
test("select field multiple choice formData", () => {
const schema: RJSFSchema = {
type: "array",
items: {
type: "string",
enum: ["foo", "bar", "fuzz", "qux"],
},
uniqueItems: true,
};
const formData = ["foo", "bar"];
const tree = renderer
.create(
<Form schema={schema} formData={formData} validator={validator} />
)
.toJSON();
expect(tree).toMatchSnapshot();
});
test("checkbox field", () => {
const schema: RJSFSchema = {
type: "boolean",
Expand Down

0 comments on commit cb27360

Please sign in to comment.