@codecademy/gamut 72.2.3-alpha.e393cd.0 → 72.2.3-alpha.f0a032.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,28 +1,14 @@
1
1
  import { useTheme } from '@emotion/react';
2
- import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
2
+ import { useId, useMemo, useRef, useState } from 'react';
3
3
  import * as React from 'react';
4
- import { parseOptions } from '../utils';
5
- import { AbbreviatedSingleValue, CustomContainer, CustomInput, CustomValueContainer, DropdownButton, formatGroupLabel, formatOptionLabel, IconOption, MultiValueRemoveButton, MultiValueWithColorMode, onFocus, RemoveAllButton, SelectDropdownContext, TypedReactSelect } from './elements';
6
- import { getMemoizedStyles } from './styles';
7
- import { filterValueFromOptions, getCreatedOptionValue, isMultipleSelectProps, isOptionsGrouped, isSingleSelectProps, removeValueFromSelectedOptions } from './utils';
4
+ import { onFocus } from './core/accessibility';
5
+ import { defaultComponents } from './core/constants';
6
+ import { getMemoizedStyles } from './core/styles';
7
+ import { resolveNoOptionsMessage } from './core/utils';
8
+ import { formatGroupLabel, formatOptionLabel, SelectDropdownContext, TypedReactSelect } from './elements';
9
+ import { useSelectHandlers } from './hooks/useSelectHandlers';
10
+ import { useSelectOptions } from './hooks/useSelectOptions';
8
11
  import { jsx as _jsx } from "react/jsx-runtime";
9
- const defaultProps = {
10
- name: undefined,
11
- components: {
12
- DropdownIndicator: DropdownButton,
13
- IndicatorSeparator: () => null,
14
- ClearIndicator: RemoveAllButton,
15
- SelectContainer: CustomContainer,
16
- ValueContainer: CustomValueContainer,
17
- MultiValue: MultiValueWithColorMode,
18
- MultiValueRemove: MultiValueRemoveButton,
19
- Option: IconOption,
20
- SingleValue: AbbreviatedSingleValue,
21
- Input: CustomInput
22
- }
23
- };
24
- const onChangeAction = 'select-option';
25
-
26
12
  /**
27
13
  * A flexible dropdown select component built on top of react-select.
28
14
  *
@@ -99,87 +85,32 @@ export const SelectDropdown = ({
99
85
  const isSearchable = isCreatable || isSearchableProp;
100
86
  const rawInputId = useId();
101
87
  const inputId = name ?? `${id}-select-dropdown-${rawInputId}`;
102
- const [activated, setActivated] = useState(false);
103
- const [currentFocusedValue, setCurrentFocusedValue] = useState(undefined);
104
-
105
- // these are used to programatically manage the focus state of our multi-select options + 'Remove all' button
106
88
  const removeAllButtonRef = useRef(null);
107
89
  const selectInputRef = useRef(null);
108
- const selectOptions = useMemo(() => {
109
- if (!options || Array.isArray(options) && !options.length || typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length === 0) {
110
- return [];
111
- }
112
- if (isOptionsGrouped(options)) {
113
- return options;
114
- }
115
- return parseOptions({
116
- options,
117
- id,
118
- size
119
- });
120
- }, [options, id, size]);
121
- const parsedValue = useMemo(() => {
122
- if (isOptionsGrouped(selectOptions)) {
123
- for (const group of selectOptions) {
124
- if (group.options) {
125
- const foundOption = group.options.find(option => option.value === value);
126
- if (foundOption) return foundOption;
127
- }
128
- }
129
- return undefined;
130
- }
131
- return selectOptions.find(option => option.value === value);
132
- }, [selectOptions, value]);
133
- const [multiValues, setMultiValues] = useState(multiple &&
134
- // To keep this efficient for non-multiSelect
135
- filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions)));
136
-
137
- // Sync multi-select value from props when controlled (`value` is a string[]).
138
- // Uncontrolled multi (`value` undefined or '') keeps selection in local state.
139
- useEffect(() => {
140
- if (!multiple || !Array.isArray(value)) return;
141
- const newMultiValues = filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions));
142
- if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
143
-
144
- // We only update this when our passed in options or value changes, not multiValues.
145
- // eslint-disable-next-line react-hooks/exhaustive-deps
146
- }, [options, value, multiple]);
147
- const changeHandler = useCallback((optionEvent, actionMeta) => {
148
- setActivated(true);
149
- if (actionMeta.action === 'create-option') {
150
- const createdValue = getCreatedOptionValue(optionEvent, actionMeta, multiple);
151
- if (createdValue) {
152
- onCreateOption?.(createdValue);
153
- }
154
- }
155
- const onChangeProps = {
156
- onChange,
157
- multiple
158
- };
159
- const forwardedMeta = actionMeta.action === 'create-option' ? actionMeta : {
160
- action: onChangeAction,
161
- option: isMultipleSelectProps(onChangeProps) ? undefined : optionEvent
162
- };
163
- if (isSingleSelectProps(onChangeProps)) {
164
- const singleOptionEvent = optionEvent;
165
- onChangeProps.onChange?.(singleOptionEvent, forwardedMeta);
166
- }
167
- if (isMultipleSelectProps(onChangeProps)) {
168
- setMultiValues(optionEvent);
169
- onChangeProps.onChange?.(optionEvent, forwardedMeta);
170
- }
171
- }, [onChange, multiple, onCreateOption]);
172
- const keyPressHandler = e => {
173
- if (multiple && e.key === 'Enter' && currentFocusedValue && multiValues) {
174
- const newMultiValues = removeValueFromSelectedOptions(multiValues, currentFocusedValue);
175
- if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
176
- }
177
- if (removeAllButtonRef.current !== null && e.key === 'ArrowRight' && multiValues && currentFocusedValue === multiValues[multiValues.length - 1].value) {
178
- removeAllButtonRef.current.focus();
179
- }
180
- };
181
- const noOptionsMessage = validationMessage === undefined ? undefined // fall back to react-select default ("No options")
182
- : typeof validationMessage === 'function' ? validationMessage : () => validationMessage;
90
+ const [currentFocusedValue, setCurrentFocusedValue] = useState(undefined);
91
+ const {
92
+ selectOptions,
93
+ parsedValue
94
+ } = useSelectOptions({
95
+ options,
96
+ id,
97
+ size,
98
+ value: value
99
+ });
100
+ const {
101
+ activated,
102
+ multiValues,
103
+ changeHandler,
104
+ keyPressHandler
105
+ } = useSelectHandlers({
106
+ onChange,
107
+ multiple,
108
+ onCreateOption,
109
+ selectOptions,
110
+ value,
111
+ currentFocusedValue,
112
+ removeAllButtonRef
113
+ });
183
114
  const theme = useTheme();
184
115
  const memoizedStyles = useMemo(() => {
185
116
  return getMemoizedStyles(theme, zIndex);
@@ -192,12 +123,12 @@ export const SelectDropdown = ({
192
123
  selectInputRef
193
124
  },
194
125
  children: /*#__PURE__*/_jsx(TypedReactSelect, {
195
- ...defaultProps,
196
126
  activated: activated,
197
127
  "aria-live": "assertive",
198
128
  ariaLiveMessages: {
199
129
  onFocus
200
130
  },
131
+ components: defaultComponents,
201
132
  dropdownWidth: dropdownWidth,
202
133
  error: Boolean(error),
203
134
  formatCreateLabel: formatCreateLabel,
@@ -217,7 +148,7 @@ export const SelectDropdown = ({
217
148
  isValidNewOption: isValidNewOption,
218
149
  menuAlignment: menuAlignment,
219
150
  name: name,
220
- noOptionsMessage: noOptionsMessage,
151
+ noOptionsMessage: resolveNoOptionsMessage(validationMessage),
221
152
  options: selectOptions,
222
153
  placeholder: placeholder,
223
154
  selectRef: selectInputRef,
@@ -227,7 +158,7 @@ export const SelectDropdown = ({
227
158
  value: multiple ? multiValues : parsedValue,
228
159
  onChange: changeHandler,
229
160
  onInputChange: onInputChange,
230
- onKeyDown: multiple ? e => keyPressHandler(e) : undefined,
161
+ onKeyDown: multiple ? keyPressHandler : undefined,
231
162
  ...rest
232
163
  })
233
164
  });
@@ -0,0 +1,3 @@
1
+ import { AriaOnFocus } from 'react-select';
2
+ import { ExtendedOption } from '../types';
3
+ export declare const onFocus: AriaOnFocus<ExtendedOption>;
@@ -0,0 +1,12 @@
1
+ export const onFocus = ({
2
+ focused: {
3
+ label,
4
+ subtitle,
5
+ rightLabel,
6
+ disabled
7
+ }
8
+ }) => {
9
+ const formattedSubtitle = `, ${subtitle}`;
10
+ const formattedRightLabel = `, ${rightLabel}`;
11
+ return `You are currently focused on option ${label}${subtitle ? formattedSubtitle : ''} ${rightLabel ? formattedRightLabel : ''}${disabled ? ', disabled' : ''}`;
12
+ };
@@ -0,0 +1,13 @@
1
+ export declare const defaultComponents: {
2
+ DropdownIndicator: (props: import("../types").SizedIndicatorProps) => import("react/jsx-runtime").JSX.Element;
3
+ IndicatorSeparator: () => null;
4
+ ClearIndicator: (props: import("../types").SizedIndicatorProps) => import("react/jsx-runtime").JSX.Element;
5
+ SelectContainer: ({ children, ...rest }: import("../types").CustomSelectComponentProps<(<Option_18, IsMulti_18 extends boolean, Group_18 extends import("react-select").GroupBase<Option_18>>(props: import("react-select").ContainerProps<Option_18, IsMulti_18, Group_18>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
6
+ ValueContainer: ({ ...rest }: import("../types").CustomSelectComponentProps<(<Option_20, IsMulti_20 extends boolean, Group_20 extends import("react-select").GroupBase<Option_20>>(props: import("react-select").ValueContainerProps<Option_20, IsMulti_20, Group_20>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
7
+ MultiValue: (props: import("react-select").MultiValueProps<import("..").ExtendedOption, true, import("react-select").GroupBase<import("..").ExtendedOption>>) => import("react/jsx-runtime").JSX.Element;
8
+ MultiValueRemove: (props: import("react-select").MultiValueRemoveProps<import("..").ExtendedOption, true, import("react-select").GroupBase<import("..").ExtendedOption>>) => import("react/jsx-runtime").JSX.Element;
9
+ Option: ({ children, ...rest }: import("../types").CustomSelectComponentProps<(<Option_16, IsMulti_16 extends boolean, Group_16 extends import("react-select").GroupBase<Option_16>>(props: import("react-select").OptionProps<Option_16, IsMulti_16, Group_16>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
10
+ SingleValue: (props: import("react-select").SingleValueProps<import("..").ExtendedOption, false>) => import("react/jsx-runtime").JSX.Element;
11
+ Input: ({ ...rest }: import("../types").CustomSelectComponentProps<(<Option_7, IsMulti_7 extends boolean, Group_7 extends import("react-select").GroupBase<Option_7>>(props: import("react-select").InputProps<Option_7, IsMulti_7, Group_7>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
12
+ };
13
+ export declare const ON_CHANGE_ACTION: "select-option";
@@ -0,0 +1,14 @@
1
+ import { AbbreviatedSingleValue, CustomContainer, CustomInput, CustomValueContainer, DropdownButton, IconOption, MultiValueRemoveButton, MultiValueWithColorMode, RemoveAllButton } from '../elements';
2
+ export const defaultComponents = {
3
+ DropdownIndicator: DropdownButton,
4
+ IndicatorSeparator: () => null,
5
+ ClearIndicator: RemoveAllButton,
6
+ SelectContainer: CustomContainer,
7
+ ValueContainer: CustomValueContainer,
8
+ MultiValue: MultiValueWithColorMode,
9
+ MultiValueRemove: MultiValueRemoveButton,
10
+ Option: IconOption,
11
+ SingleValue: AbbreviatedSingleValue,
12
+ Input: CustomInput
13
+ };
14
+ export const ON_CHANGE_ACTION = 'select-option';
@@ -1,6 +1,6 @@
1
1
  import { css, states, variant } from '@codecademy/gamut-styles';
2
- import { dismissSharedStyles, tagBaseStyles, tagLabelFontSize, tagLabelPadding } from '../../Tag/styles';
3
- import { formBaseComponentStyles, formBaseFieldStylesObject, formFieldDisabledStyles, formFieldPaddingStyles, InputSelectors } from '../styles';
2
+ import { dismissSharedStyles, tagBaseStyles, tagLabelFontSize, tagLabelPadding } from '../../../Tag/styles';
3
+ import { formBaseComponentStyles, formBaseFieldStylesObject, formFieldDisabledStyles, formFieldPaddingStyles, InputSelectors } from '../../styles';
4
4
  const selectDropdownStyles = css({
5
5
  ...formBaseFieldStylesObject,
6
6
  display: 'flex',
@@ -1,6 +1,7 @@
1
+ import * as React from 'react';
1
2
  import { ActionMeta, Options as OptionsType } from 'react-select';
2
- import { SelectOptionBase } from '../utils';
3
- import { BaseOnChangeProps, ExtendedOption, MultiSelectDropdownProps, OptionStrict, SelectDropdownGroup, SelectDropdownOptions, SelectDropdownProps, SingleSelectDropdownProps } from './types';
3
+ import { SelectOptionBase } from '../../utils';
4
+ import { BaseOnChangeProps, ExtendedOption, MultiSelectDropdownProps, OptionStrict, SelectDropdownGroup, SelectDropdownOptions, SelectDropdownProps, SingleSelectDropdownProps } from '../types';
4
5
  export declare const isMultipleSelectProps: (props: BaseOnChangeProps) => props is MultiSelectDropdownProps;
5
6
  export declare const isSingleSelectProps: (props: BaseOnChangeProps) => props is SingleSelectDropdownProps;
6
7
  /**
@@ -29,4 +30,7 @@ export declare const filterValueFromOptions: (options: SelectOptionBase[] | Sele
29
30
  * @param value - The value or values to remove
30
31
  * @returns New array with the specified values removed
31
32
  */
33
+ export declare const resolveNoOptionsMessage: (validationMessage: SelectDropdownProps["validationMessage"]) => ((obj: {
34
+ inputValue: string;
35
+ }) => React.ReactNode) | undefined;
32
36
  export declare const removeValueFromSelectedOptions: (selectedOptions: ExtendedOption[] | SelectOptionBase[], value: SelectDropdownProps["value"]) => SelectOptionBase[];
@@ -44,6 +44,13 @@ export const filterValueFromOptions = (options, value, optionsAreGrouped) => {
44
44
  * @param value - The value or values to remove
45
45
  * @returns New array with the specified values removed
46
46
  */
47
+ export const resolveNoOptionsMessage = validationMessage => {
48
+ if (validationMessage === undefined) return undefined;
49
+ if (typeof validationMessage === 'function') {
50
+ return validationMessage;
51
+ }
52
+ return () => validationMessage;
53
+ };
47
54
  export const removeValueFromSelectedOptions = (selectedOptions, value) => {
48
55
  return selectedOptions.filter(option => {
49
56
  if (Array.isArray(value)) {
@@ -1,13 +1,4 @@
1
- import { AriaOnFocus } from 'react-select';
2
- import { ExtendedOption, SizedIndicatorProps } from '../types';
3
- /**
4
- * Generates accessible focus messages for screen readers.
5
- * Provides detailed information about the currently focused option.
6
- *
7
- * @param params - Object containing the focused option details
8
- * @returns Formatted accessibility message
9
- */
10
- export declare const onFocus: AriaOnFocus<ExtendedOption>;
1
+ import { SizedIndicatorProps } from '../types';
11
2
  /**
12
3
  * Custom dropdown indicator that shows either a chevron or search icon.
13
4
  * The icon type depends on whether the select is searchable or not.
@@ -5,27 +5,6 @@ const {
5
5
  DropdownIndicator
6
6
  } = SelectDropdownElements;
7
7
 
8
- /**
9
- * Generates accessible focus messages for screen readers.
10
- * Provides detailed information about the currently focused option.
11
- *
12
- * @param params - Object containing the focused option details
13
- * @returns Formatted accessibility message
14
- */
15
- export const onFocus = ({
16
- focused: {
17
- label,
18
- subtitle,
19
- rightLabel,
20
- disabled
21
- }
22
- }) => {
23
- const formattedSubtitle = `, ${subtitle}`;
24
- const formattedRightLabel = `, ${rightLabel}`;
25
- const msg = `You are currently focused on option ${label}${subtitle ? formattedSubtitle : ''} ${rightLabel ? formattedRightLabel : ''}${disabled ? ', disabled' : ''}`;
26
- return msg;
27
- };
28
-
29
8
  /**
30
9
  * Custom dropdown indicator that shows either a chevron or search icon.
31
10
  * The icon type depends on whether the select is searchable or not.
@@ -1,5 +1,6 @@
1
1
  export { iconSize, selectedIconSize, indicatorIcons } from './constants';
2
2
  export { MultiValueWithColorMode, MultiValueRemoveButton, RemoveAllButton, } from './multi-value';
3
- export { DropdownButton, onFocus } from './controls';
3
+ export { DropdownButton } from './controls';
4
+ export { onFocus } from '../core/accessibility';
4
5
  export { SelectDropdownContext, CustomContainer, CustomInput, CustomValueContainer, TypedReactSelect, } from './containers';
5
6
  export { IconOption, AbbreviatedSingleValue, formatOptionLabel, formatGroupLabel, } from './options';
@@ -1,5 +1,6 @@
1
1
  export { iconSize, selectedIconSize, indicatorIcons } from './constants';
2
2
  export { MultiValueWithColorMode, MultiValueRemoveButton, RemoveAllButton } from './multi-value';
3
- export { DropdownButton, onFocus } from './controls';
3
+ export { DropdownButton } from './controls';
4
+ export { onFocus } from '../core/accessibility';
4
5
  export { SelectDropdownContext, CustomContainer, CustomInput, CustomValueContainer, TypedReactSelect } from './containers';
5
6
  export { IconOption, AbbreviatedSingleValue, formatOptionLabel, formatGroupLabel } from './options';
@@ -0,0 +1,22 @@
1
+ import { KeyboardEvent } from 'react';
2
+ import * as React from 'react';
3
+ import { ActionMeta, Options as OptionsType } from 'react-select';
4
+ import { SelectOptionBase } from '../../utils';
5
+ import { MultiSelectDropdownProps, OptionStrict, SelectDropdownGroup, SelectDropdownProps, SingleSelectDropdownProps } from '../types';
6
+ interface UseSelectHandlersArgs {
7
+ onChange?: SingleSelectDropdownProps['onChange'] | MultiSelectDropdownProps['onChange'];
8
+ multiple?: boolean;
9
+ onCreateOption?: (inputValue: string) => void;
10
+ selectOptions: SelectOptionBase[] | SelectDropdownGroup[];
11
+ value?: SelectDropdownProps['value'];
12
+ currentFocusedValue: unknown;
13
+ removeAllButtonRef: React.MutableRefObject<HTMLDivElement | null>;
14
+ }
15
+ interface UseSelectHandlersReturn {
16
+ activated: boolean;
17
+ multiValues: OptionStrict[] | false;
18
+ changeHandler: (optionEvent: OptionStrict | OptionsType<OptionStrict>, actionMeta: ActionMeta<OptionStrict>) => void;
19
+ keyPressHandler: (e: KeyboardEvent<HTMLDivElement>) => void;
20
+ }
21
+ export declare const useSelectHandlers: ({ onChange, multiple, onCreateOption, selectOptions, value, currentFocusedValue, removeAllButtonRef, }: UseSelectHandlersArgs) => UseSelectHandlersReturn;
22
+ export {};
@@ -0,0 +1,62 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { ON_CHANGE_ACTION } from '../core/constants';
3
+ import { filterValueFromOptions, getCreatedOptionValue, isMultipleSelectProps, isOptionsGrouped, isSingleSelectProps, removeValueFromSelectedOptions } from '../core/utils';
4
+ export const useSelectHandlers = ({
5
+ onChange,
6
+ multiple,
7
+ onCreateOption,
8
+ selectOptions,
9
+ value,
10
+ currentFocusedValue,
11
+ removeAllButtonRef
12
+ }) => {
13
+ const [activated, setActivated] = useState(false);
14
+ const [multiValues, setMultiValues] = useState(multiple ? filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions)) : false);
15
+
16
+ // Sync multi-select value from props when controlled (`value` is a string[]).
17
+ // Uncontrolled multi (`value` undefined or '') keeps selection in local state.
18
+ useEffect(() => {
19
+ if (!multiple || !Array.isArray(value)) return;
20
+ const newMultiValues = filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions));
21
+ if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
22
+
23
+ // eslint-disable-next-line react-hooks/exhaustive-deps
24
+ }, [selectOptions, value, multiple]);
25
+ const changeHandler = useCallback((optionEvent, actionMeta) => {
26
+ setActivated(true);
27
+ if (actionMeta.action === 'create-option') {
28
+ const createdValue = getCreatedOptionValue(optionEvent, actionMeta, multiple);
29
+ if (createdValue) onCreateOption?.(createdValue);
30
+ }
31
+ const onChangeProps = {
32
+ onChange,
33
+ multiple
34
+ };
35
+ const forwardedMeta = actionMeta.action === 'create-option' ? actionMeta : {
36
+ action: ON_CHANGE_ACTION,
37
+ option: isMultipleSelectProps(onChangeProps) ? undefined : optionEvent
38
+ };
39
+ if (isSingleSelectProps(onChangeProps)) {
40
+ onChangeProps.onChange?.(optionEvent, forwardedMeta);
41
+ }
42
+ if (isMultipleSelectProps(onChangeProps)) {
43
+ setMultiValues(optionEvent);
44
+ onChangeProps.onChange?.(optionEvent, forwardedMeta);
45
+ }
46
+ }, [onChange, multiple, onCreateOption]);
47
+ const keyPressHandler = e => {
48
+ if (multiple && e.key === 'Enter' && currentFocusedValue && multiValues) {
49
+ const newMultiValues = removeValueFromSelectedOptions(multiValues, currentFocusedValue);
50
+ if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
51
+ }
52
+ if (removeAllButtonRef.current !== null && e.key === 'ArrowRight' && multiValues && currentFocusedValue === multiValues[multiValues.length - 1].value) {
53
+ removeAllButtonRef.current.focus();
54
+ }
55
+ };
56
+ return {
57
+ activated,
58
+ multiValues,
59
+ changeHandler,
60
+ keyPressHandler
61
+ };
62
+ };
@@ -0,0 +1,14 @@
1
+ import { SelectOptionBase } from '../../utils';
2
+ import { SelectDropdownGroup, SelectDropdownOptions, SelectDropdownSizes } from '../types';
3
+ interface UseSelectOptionsArgs {
4
+ options?: SelectDropdownOptions | SelectDropdownGroup[];
5
+ id?: string;
6
+ size?: SelectDropdownSizes['size'];
7
+ value?: string | string[];
8
+ }
9
+ interface UseSelectOptionsReturn {
10
+ selectOptions: SelectOptionBase[] | SelectDropdownGroup[];
11
+ parsedValue: SelectOptionBase | undefined;
12
+ }
13
+ export declare const useSelectOptions: ({ options, id, size, value, }: UseSelectOptionsArgs) => UseSelectOptionsReturn;
14
+ export {};
@@ -0,0 +1,39 @@
1
+ import { useMemo } from 'react';
2
+ import { parseOptions } from '../../utils';
3
+ import { isOptionsGrouped } from '../core/utils';
4
+ export const useSelectOptions = ({
5
+ options,
6
+ id,
7
+ size,
8
+ value
9
+ }) => {
10
+ const selectOptions = useMemo(() => {
11
+ if (!options || Array.isArray(options) && !options.length || typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length === 0) {
12
+ return [];
13
+ }
14
+ if (isOptionsGrouped(options)) {
15
+ return options;
16
+ }
17
+ return parseOptions({
18
+ options,
19
+ id,
20
+ size
21
+ });
22
+ }, [options, id, size]);
23
+ const parsedValue = useMemo(() => {
24
+ if (isOptionsGrouped(selectOptions)) {
25
+ for (const group of selectOptions) {
26
+ if (group.options) {
27
+ const foundOption = group.options.find(option => option.value === value);
28
+ if (foundOption) return foundOption;
29
+ }
30
+ }
31
+ return undefined;
32
+ }
33
+ return selectOptions.find(option => option.value === value);
34
+ }, [selectOptions, value]);
35
+ return {
36
+ selectOptions,
37
+ parsedValue
38
+ };
39
+ };
@@ -14,7 +14,7 @@ export type InternalSelectProps = {
14
14
  * Ref type for programmatic focus management.
15
15
  * Used for managing focus on select input and remove all button.
16
16
  */
17
- export type ProgramaticFocusRef = React.MutableRefObject<HTMLDivElement> | React.MutableRefObject<null>;
17
+ export type ProgrammaticFocusRef = React.MutableRefObject<HTMLDivElement> | React.MutableRefObject<null>;
18
18
  /**
19
19
  * Context value for SelectDropdown internal state management.
20
20
  * Provides access to focus state and refs for keyboard navigation.
@@ -25,9 +25,9 @@ export interface SelectDropdownContextValueTypes {
25
25
  /** Function to update the currently focused value */
26
26
  setCurrentFocusedValue?: React.Dispatch<React.SetStateAction<unknown>>;
27
27
  /** Ref to the select input for programmatic focus */
28
- selectInputRef?: ProgramaticFocusRef;
28
+ selectInputRef?: ProgrammaticFocusRef;
29
29
  /** Ref to the remove all button for programmatic focus */
30
- removeAllButtonRef?: ProgramaticFocusRef;
30
+ removeAllButtonRef?: ProgrammaticFocusRef;
31
31
  }
32
32
  /**
33
33
  * Props for sized indicator components (dropdown arrow, search icon, etc.).
@@ -1,5 +1,5 @@
1
1
  import { StyleProps } from '@codecademy/variance';
2
- import { conditionalBorderStates } from '../styles';
2
+ import { conditionalBorderStates } from '../core/styles';
3
3
  import { InternalInputsProps } from './component-props';
4
4
  /**
5
5
  * Size variants for the SelectDropdown component.
@@ -1,4 +1,4 @@
1
1
  export * from './shared-system-props';
2
2
  export * from './Checkbox-styles';
3
3
  export * from './Radio-styles';
4
- export * from '../SelectDropdown/styles';
4
+ export * from '../SelectDropdown/core/styles';
@@ -1,4 +1,4 @@
1
1
  export * from './shared-system-props';
2
2
  export * from './Checkbox-styles';
3
3
  export * from './Radio-styles';
4
- export * from '../SelectDropdown/styles';
4
+ export * from '../SelectDropdown/core/styles';
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@codecademy/gamut",
3
3
  "description": "Styleguide & Component library for Codecademy",
4
- "version": "72.2.3-alpha.e393cd.0",
4
+ "version": "72.2.3-alpha.f0a032.0",
5
5
  "author": "Codecademy Engineering <dev@codecademy.com>",
6
6
  "bin": "./bin/gamut.mjs",
7
7
  "dependencies": {
8
- "@codecademy/gamut-icons": "9.57.10-alpha.e393cd.0",
9
- "@codecademy/gamut-illustrations": "0.58.16-alpha.e393cd.0",
10
- "@codecademy/gamut-patterns": "0.10.35-alpha.e393cd.0",
11
- "@codecademy/gamut-styles": "20.0.3-alpha.e393cd.0",
12
- "@codecademy/variance": "0.26.2-alpha.e393cd.0",
8
+ "@codecademy/gamut-icons": "9.57.10-alpha.f0a032.0",
9
+ "@codecademy/gamut-illustrations": "0.58.16-alpha.f0a032.0",
10
+ "@codecademy/gamut-patterns": "0.10.35-alpha.f0a032.0",
11
+ "@codecademy/gamut-styles": "20.0.3-alpha.f0a032.0",
12
+ "@codecademy/variance": "0.26.2-alpha.f0a032.0",
13
13
  "@formatjs/intl-locale": "5.3.1",
14
14
  "@react-aria/interactions": "3.25.0",
15
15
  "@types/marked": "^4.0.8",