Skip to content

Commit

Permalink
fix: allow empty string value for option value and value prop (#2104)
Browse files Browse the repository at this point in the history
* fix: allow empty string value for option value and value prop
  • Loading branch information
chrispulsinelli-okta committed Feb 1, 2024
1 parent 6396df8 commit c1d5f1b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 5 deletions.
24 changes: 19 additions & 5 deletions packages/odyssey-react-mui/src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
useInputValues,
getControlState,
} from "./inputUtils";
import { normalizedKey } from "./useNormalizedKey";

export type SelectOption = {
text: string;
Expand Down Expand Up @@ -215,7 +216,10 @@ const Select = <
typeof option === "object"
? {
text: option.text,
value: option.value || option.text,
value:
option?.value === ""
? option.value
: option.value || option.text,
type: option.type === "heading" ? "heading" : "option",
}
: { text: option, value: option, type: "option" }
Expand Down Expand Up @@ -261,19 +265,25 @@ const Select = <
// that will populate the <Select>
const children = useMemo(
() =>
normalizedOptions.map((option) => {
normalizedOptions.map((option, index) => {
if (option.type === "heading") {
return <ListSubheader key={option.text}>{option.text}</ListSubheader>;
}
return (
<MenuItem key={option.value} value={option.value}>
<MenuItem
key={normalizedKey(option.text, index.toString())}
value={option.value}
>
{hasMultipleChoices && (
<MuiCheckbox
checked={internalSelectedValues?.includes(option.value)}
checked={
option.value !== undefined &&
internalSelectedValues?.includes(option.value)
}
/>
)}
{option.text}
{internalSelectedValues === option.value && (
{internalSelectedValues === option?.value && (
<ListItemSecondaryAction>
<CheckIcon />
</ListItemSecondaryAction>
Expand All @@ -291,6 +301,10 @@ const Select = <
aria-describedby={ariaDescribedBy}
aria-errormessage={errorMessageElementId}
children={children}
data-se={testId}
displayEmpty={
inputValues?.value === "" || inputValues?.defaultValue === ""
}
id={id}
inputProps={{ "data-se": testId }}
inputRef={localInputRef}
Expand Down
17 changes: 17 additions & 0 deletions packages/odyssey-react-mui/src/useNormalizedKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*!
* Copyright (c) 2024-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/

export const normalizedKey = (...keyParts: string[]): string => {
const SEPARATOR = "-";
// Joins all strings together with SEPARATOR, replaces any non-alphanumeric character with SEPARATOR and casts all to lowercase
return keyParts.join(SEPARATOR).replace(/\W+/g, SEPARATOR).toLowerCase();
};
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,22 @@ export const ControlledPreselectedMultipleSelect: StoryObj<typeof Select> = {
return <Select {...props} value={localValue} onChange={onChange} />;
},
};

export const ControlledEmptyValue: StoryObj<typeof Select> = {
args: {
value: "",
options: [
{ value: "", text: "Default option" },
{ value: "value1", text: "Value 1" },
{ value: "value2", text: "Value 2" },
],
},
render: function C(props) {
const [localValue, setLocalValue] = useState("");
const onChange = useCallback(
(event) => setLocalValue(event.target.value),
[]
);
return <Select {...props} value={localValue} onChange={onChange} />;
},
};

0 comments on commit c1d5f1b

Please sign in to comment.