Skip to content

Commit a8a99c9

Browse files
authored
Merge branch 'main' into np-gm-1107
2 parents e43478d + 3e66b16 commit a8a99c9

40 files changed

+3189
-1333
lines changed

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"version": "1.2.0",
55
"author": "Codecademy Engineers <[email protected]>",
66
"dependencies": {
7-
"@emotion/react": "11.11.1",
8-
"@emotion/styled": "11.11.0",
7+
"@emotion/react": "11.14.0",
8+
"@emotion/styled": "11.14.0",
99
"@vidstack/react": "^1.12.12",
1010
"core-js": "3.7.0",
1111
"lodash": "^4.17.5",
@@ -113,7 +113,8 @@
113113
"private": true,
114114
"repository": "[email protected]:Codecademy/gamut.git",
115115
"resolutions": {
116-
"@typescript-eslint/utils": "^5.15.0"
116+
"@typescript-eslint/utils": "^5.15.0",
117+
"error-ex": "1.3.4"
117118
},
118119
"scripts": {
119120
"build": "nx run-many --target=build --all",

packages/gamut-icons/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [9.50.0](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-15)
7+
8+
### Features
9+
10+
- **Icon:** added new file-text-info icon. ([a06aeb4](https://github.com/Codecademy/gamut/commit/a06aeb44d6a9429eac7bc28d3483a690dbabf3bd))
11+
612
### [9.49.2](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-08-20)
713

814
**Note:** Version bump only for package @codecademy/gamut-icons

packages/gamut-icons/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@codecademy/gamut-icons",
33
"description": "Icon library for codecademy.com",
4-
"version": "9.49.2",
4+
"version": "9.50.0",
55
"author": "Codecademy <[email protected]>",
66
"dependencies": {
77
"@codecademy/gamut-styles": "17.9.0",
Lines changed: 3 additions & 0 deletions
Loading

packages/gamut-kit/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
### [0.6.545](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-23)
7+
8+
**Note:** Version bump only for package @codecademy/gamut-kit
9+
10+
### [0.6.544](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-22)
11+
12+
**Note:** Version bump only for package @codecademy/gamut-kit
13+
14+
### [0.6.543](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-15)
15+
16+
**Note:** Version bump only for package @codecademy/gamut-kit
17+
18+
### [0.6.542](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-12)
19+
20+
**Note:** Version bump only for package @codecademy/gamut-kit
21+
622
### [0.6.541](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-09)
723

824
**Note:** Version bump only for package @codecademy/gamut-kit

packages/gamut-kit/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"name": "@codecademy/gamut-kit",
33
"description": "Styleguide & Component library for Codecademy",
4-
"version": "0.6.541",
4+
"version": "0.6.545",
55
"author": "Codecademy Engineering <[email protected]>",
66
"dependencies": {
7-
"@codecademy/gamut": "66.3.1",
8-
"@codecademy/gamut-icons": "9.49.2",
7+
"@codecademy/gamut": "66.5.0",
8+
"@codecademy/gamut-icons": "9.50.0",
99
"@codecademy/gamut-illustrations": "0.54.9",
1010
"@codecademy/gamut-patterns": "0.10.15",
1111
"@codecademy/gamut-styles": "17.9.0",

packages/gamut/CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [66.5.0](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-23)
7+
8+
### Features
9+
10+
- add attrs to combobox ([0e49416](https://github.com/Codecademy/gamut/commit/0e49416a19c958874dbbee6ac9d3ba8c40c27c6c))
11+
12+
### [66.4.2](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-22)
13+
14+
**Note:** Version bump only for package @codecademy/gamut
15+
16+
### [66.4.1](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-15)
17+
18+
**Note:** Version bump only for package @codecademy/gamut
19+
20+
## [66.4.0](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-12)
21+
22+
### Features
23+
24+
- add abbreviations to SelectDropdown, fixed grouped select + formatting issues ([1f01312](https://github.com/Codecademy/gamut/commit/1f013125d104d24d7b9f9b1fca275a49812ebda0))
25+
626
### [66.3.1](https://github.com/Codecademy/gamut/compare/@codecademy/[email protected]...@codecademy/[email protected]) (2025-09-09)
727

828
**Note:** Version bump only for package @codecademy/gamut

packages/gamut/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "@codecademy/gamut",
33
"description": "Styleguide & Component library for Codecademy",
4-
"version": "66.3.1",
4+
"version": "66.5.0",
55
"author": "Codecademy Engineering <[email protected]>",
66
"dependencies": {
7-
"@codecademy/gamut-icons": "9.49.2",
7+
"@codecademy/gamut-icons": "9.50.0",
88
"@codecademy/gamut-illustrations": "0.54.9",
99
"@codecademy/gamut-patterns": "0.10.15",
1010
"@codecademy/gamut-styles": "17.9.0",

packages/gamut/src/Disclosure/elements.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,17 @@ export const StyledFillButton = styled(FillButton)(
103103
})
104104
);
105105

106-
export type DisclosureBodyWrapperStyles = StyleProps<
107-
typeof disclosureBodyWrapperStates
108-
>;
109-
110106
const disclosureBodyWrapperStates = states({
111107
hasPanelBg: {
112108
bg: 'background-selected',
113109
p: 8,
114110
},
115111
});
116112

113+
export type DisclosureBodyWrapperStyles = StyleProps<
114+
typeof disclosureBodyWrapperStates
115+
>;
116+
117117
export const DisclosureBodyWrapper = styled(
118118
FlexBox
119119
)<DisclosureBodyWrapperStyles>(disclosureBodyWrapperStates);

packages/gamut/src/Form/SelectDropdown/SelectDropdown.tsx

Lines changed: 115 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ import {
1111
import * as React from 'react';
1212
import { Options as OptionsType, StylesConfig } from 'react-select';
1313

14-
import { parseOptions } from '../utils';
14+
import { parseOptions, SelectOptionBase } from '../utils';
1515
import {
16+
AbbreviatedSingleValue,
1617
CustomContainer,
18+
CustomInput,
19+
CustomValueContainer,
1720
DropdownButton,
1821
formatGroupLabel,
1922
formatOptionLabel,
@@ -26,11 +29,15 @@ import {
2629
TypedReactSelect,
2730
} from './elements';
2831
import { getMemoizedStyles } from './styles';
29-
import { ExtendedOption, OptionStrict, SelectDropdownProps } from './types';
32+
import {
33+
OptionStrict,
34+
SelectDropdownGroup,
35+
SelectDropdownProps,
36+
} from './types';
3037
import {
3138
filterValueFromOptions,
3239
isMultipleSelectProps,
33-
isOptionGroup,
40+
isOptionsGrouped,
3441
isSingleSelectProps,
3542
removeValueFromSelectedOptions,
3643
} from './utils';
@@ -42,27 +49,79 @@ const defaultProps = {
4249
IndicatorSeparator: () => null,
4350
ClearIndicator: RemoveAllButton,
4451
SelectContainer: CustomContainer,
52+
ValueContainer: CustomValueContainer,
4553
MultiValue: MultiValueWithColorMode,
4654
MultiValueRemove: MultiValueRemoveButton,
4755
Option: IconOption,
56+
SingleValue: AbbreviatedSingleValue,
57+
Input: CustomInput,
4858
},
4959
};
5060
const onChangeAction = 'select-option';
5161

62+
/**
63+
* A flexible dropdown select component built on top of react-select.
64+
*
65+
* Supports both single and multi-select modes with customizable options including
66+
* icons, subtitles, right labels, and abbreviations. The component provides
67+
* accessibility features, keyboard navigation, and responsive styling.
68+
*
69+
* @example
70+
* ```tsx
71+
* // Basic single select
72+
* <SelectDropdown
73+
* name="country"
74+
* options={[
75+
* { label: 'United States', value: 'us' },
76+
* { label: 'Canada', value: 'ca' }
77+
* ]}
78+
* onChange={(option) => console.log(option)}
79+
* />
80+
*
81+
* // Multi-select with icons
82+
* <SelectDropdown
83+
* name="skills"
84+
* multiple
85+
* options={[
86+
* { label: 'React', value: 'react', icon: ReactIcon },
87+
* { label: 'TypeScript', value: 'ts', icon: TypeScriptIcon }
88+
* ]}
89+
* onChange={(options) => console.log(options)}
90+
* />
91+
*
92+
* // Grouped options with extended features
93+
* <SelectDropdown
94+
* name="category"
95+
* placeholder="Default placeholder text"
96+
* options={[
97+
* {
98+
* label: 'Frontend',
99+
* options: [
100+
* { label: 'React', value: 'react', subtitle: 'UI Library' },
101+
* { label: 'Vue', value: 'vue', subtitle: 'Progressive Framework' }
102+
* ]
103+
* }
104+
* ]}
105+
* />
106+
* ```
107+
*/
52108
export const SelectDropdown: React.FC<SelectDropdownProps> = ({
53-
options,
54-
id,
55-
size,
56-
error,
57109
disabled,
58-
onChange,
59-
value,
60-
name,
61-
placeholder = 'Select an option',
110+
dropdownWidth,
111+
error,
112+
id,
62113
inputProps,
63-
multiple,
114+
inputWidth,
64115
isSearchable = false,
116+
menuAlignment = 'left',
117+
multiple,
118+
name,
119+
onChange,
120+
options,
121+
placeholder = 'Select an option',
65122
shownOptionsLimit = 6,
123+
size,
124+
value,
66125
...rest
67126
}) => {
68127
const rawInputId = useId();
@@ -75,40 +134,64 @@ export const SelectDropdown: React.FC<SelectDropdownProps> = ({
75134
const removeAllButtonRef = useRef<HTMLDivElement>(null);
76135
const selectInputRef = useRef<HTMLDivElement>(null);
77136

78-
const optionsAreGrouped = useMemo(() => {
79-
if (options?.length) {
80-
return (options as any)?.some((option: any) => isOptionGroup(option));
137+
const selectOptions = useMemo(():
138+
| SelectOptionBase[]
139+
| SelectDropdownGroup[] => {
140+
if (
141+
!options ||
142+
(Array.isArray(options) && !options.length) ||
143+
(typeof options === 'object' &&
144+
!Array.isArray(options) &&
145+
Object.keys(options).length === 0)
146+
) {
147+
return [];
81148
}
82-
return false;
83-
}, [options]);
84149

85-
const selectOptions = useMemo(() => {
86-
return parseOptions({ options: options as ExtendedOption[], id, size });
150+
if (isOptionsGrouped(options)) {
151+
return options;
152+
}
153+
154+
return parseOptions({ options, id, size });
87155
}, [options, id, size]);
88156

89-
const parsedValue = useMemo(
90-
() => selectOptions.find((option) => option.value === value),
91-
[selectOptions, value]
92-
);
157+
const parsedValue = useMemo(() => {
158+
if (isOptionsGrouped(selectOptions)) {
159+
for (const group of selectOptions) {
160+
if (group.options) {
161+
const foundOption = group.options.find(
162+
(option) => option.value === value
163+
);
164+
if (foundOption) return foundOption;
165+
}
166+
}
167+
return undefined;
168+
}
169+
170+
return selectOptions.find((option) => option.value === value);
171+
}, [selectOptions, value]);
93172

94173
const [multiValues, setMultiValues] = useState(
95174
multiple && // To keep this efficient for non-multiSelect
96-
filterValueFromOptions(selectOptions, value, optionsAreGrouped)
175+
filterValueFromOptions(
176+
selectOptions,
177+
value,
178+
isOptionsGrouped(selectOptions)
179+
)
97180
);
98181

99182
// If the caller changes the initial value, let's update our value to match.
100183
useEffect(() => {
101184
const newMultiValues = filterValueFromOptions(
102185
selectOptions,
103186
value,
104-
optionsAreGrouped
187+
isOptionsGrouped(selectOptions)
105188
);
106189
if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
107190

108-
// For now, only handle the "change the value" case.
109-
// Changing the options can be looked into when this component is fleshed out (GM-354)
191+
//
192+
// We only update this when our passed in options or value changes, not multiValues.
110193
// eslint-disable-next-line react-hooks/exhaustive-deps
111-
}, [value]);
194+
}, [options, value]);
112195

113196
const changeHandler = useCallback(
114197
(optionEvent: OptionStrict | OptionsType<OptionStrict>) => {
@@ -179,16 +262,19 @@ export const SelectDropdown: React.FC<SelectDropdownProps> = ({
179262
ariaLiveMessages={{
180263
onFocus,
181264
}}
265+
dropdownWidth={dropdownWidth}
182266
error={Boolean(error)}
183267
formatGroupLabel={formatGroupLabel}
184268
formatOptionLabel={formatOptionLabel}
185269
id={id || rest.htmlFor || rawInputId}
186270
inputId={inputId}
187-
inputProps={{ ...inputProps, name }}
271+
inputProps={{ ...inputProps }}
272+
inputWidth={inputWidth}
188273
isDisabled={disabled}
189274
isMulti={multiple}
190275
isOptionDisabled={(option) => option.disabled}
191276
isSearchable={isSearchable}
277+
menuAlignment={menuAlignment}
192278
name={name}
193279
options={selectOptions}
194280
placeholder={placeholder}

0 commit comments

Comments
 (0)