Skip to content

Commit

Permalink
feat: make organization dropdown searchable
Browse files Browse the repository at this point in the history
  • Loading branch information
zawan-ila committed Apr 27, 2023
1 parent 7d0d8d2 commit 371d7de
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 69 deletions.
15 changes: 11 additions & 4 deletions src/components/CreateCoursePage/CreateCourseForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Field, reduxForm } from 'redux-form';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

import ReduxFormCreatableSelect from '../ReduxFormCreatableSelect';
import RenderInputTextField from '../RenderInputTextField';
import RenderSelectField from '../RenderSelectField';
import ActionButton from '../ActionButton';
Expand All @@ -21,6 +22,9 @@ import DateTimeField from '../DateTimeField';
import {
isSafari, localTimeZone, getDateWithDashes, getOptionsData, parseCourseTypeOptions, parseOptions,
} from '../../utils';
import { basicValidate, handleStafferOrCreateFormFail } from '../../utils/validation';

import './CreateCourseForm.scss';

class BaseCreateCourseForm extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -64,11 +68,12 @@ class BaseCreateCourseForm extends React.Component {
}

processOrganizations(organizations) {
let orgSelectList = [{ label: 'Select organization', value: '' }];
let orgSelectList = [];
if (organizations) {
const newOrgs = organizations.map(org => (
{ label: org.name, value: org.key, autoGenerateKey: org.auto_generate_course_run_keys }
));
newOrgs.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
orgSelectList = orgSelectList.concat(newOrgs);
}
return orgSelectList;
Expand Down Expand Up @@ -114,10 +119,11 @@ class BaseCreateCourseForm extends React.Component {
<form onSubmit={handleSubmit}>
<Field
name="org"
component={RenderSelectField}
component={ReduxFormCreatableSelect}
label={<FieldLabel text="Organization" />}
options={this.processOrganizations(organizations)}
label={<FieldLabel text="Organization" required />}
required
validate={basicValidate}
placeholder="Organization..."
/>
{/* TODO: Removing this field because publisher is currently only used for creating Edx courses.
We will unhide this field once we start supporting other products as well in publisher. */}
Expand Down Expand Up @@ -375,5 +381,6 @@ BaseCreateCourseForm.propTypes = {

export default reduxForm({
form: 'create-course-form',
onSubmitFail: handleStafferOrCreateFormFail,
})(BaseCreateCourseForm);
export { BaseCreateCourseForm };
19 changes: 19 additions & 0 deletions src/components/CreateCoursePage/CreateCourseForm.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.create-course-form {
// react-select specific styles
.react-select__control {
cursor: text;
border-color: #707070;
// match the height of edx-paragon form fields
height: calc(1.3333em + 1.125rem + 2px);
}

.react-select__dropdown-indicator{
color: #707070;
}

.react-select__control--menu-is-open{
.react-select__single-value{
color: #D3D3D3
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@ exports[`CreateCourseForm renders html correctly while submitting 1`] = `
helpText=""
id={null}
optional={false}
required={true}
text="Organization"
/>
}
name="org"
options={
Array [
Object {
"label": "Select organization",
"value": "",
},
Object {
"autoGenerateKey": undefined,
"label": "edX",
Expand All @@ -43,7 +38,8 @@ exports[`CreateCourseForm renders html correctly while submitting 1`] = `
},
]
}
required={true}
placeholder="Organization..."
validate={[Function]}
/>
<Field
component={[Function]}
Expand Down Expand Up @@ -407,17 +403,12 @@ exports[`CreateCourseForm renders html correctly with data 1`] = `
helpText=""
id={null}
optional={false}
required={true}
text="Organization"
/>
}
name="org"
options={
Array [
Object {
"label": "Select organization",
"value": "",
},
Object {
"autoGenerateKey": undefined,
"label": "edX",
Expand All @@ -430,7 +421,8 @@ exports[`CreateCourseForm renders html correctly with data 1`] = `
},
]
}
required={true}
placeholder="Organization..."
validate={[Function]}
/>
<Field
component={[Function]}
Expand Down Expand Up @@ -794,17 +786,12 @@ exports[`CreateCourseForm renders html correctly with no orgs 1`] = `
helpText=""
id={null}
optional={false}
required={true}
text="Organization"
/>
}
name="org"
options={
Array [
Object {
"label": "Select organization",
"value": "",
},
Object {
"autoGenerateKey": undefined,
"label": "edX",
Expand All @@ -817,7 +804,8 @@ exports[`CreateCourseForm renders html correctly with no orgs 1`] = `
},
]
}
required={true}
placeholder="Organization..."
validate={[Function]}
/>
<Field
component={[Function]}
Expand Down Expand Up @@ -1169,17 +1157,12 @@ exports[`CreateCourseForm renders html correctly with no sources 1`] = `
helpText=""
id={null}
optional={false}
required={true}
text="Organization"
/>
}
name="org"
options={
Array [
Object {
"label": "Select organization",
"value": "",
},
Object {
"autoGenerateKey": undefined,
"label": "edX",
Expand All @@ -1192,7 +1175,8 @@ exports[`CreateCourseForm renders html correctly with no sources 1`] = `
},
]
}
required={true}
placeholder="Organization..."
validate={[Function]}
/>
<Field
component={[Function]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ exports[`CreateCoursePage renders html correctly 1`] = `
initialValues={Object {}}
keepDirtyOnReinitialize={false}
onSubmit={[Function]}
onSubmitFail={[Function]}
organizations={Array []}
persistentSubmitErrors={false}
pure={true}
Expand Down Expand Up @@ -120,6 +121,7 @@ exports[`CreateCoursePage renders page correctly with course create error 1`] =
isCreating={false}
keepDirtyOnReinitialize={false}
onSubmit={[Function]}
onSubmitFail={[Function]}
organizations={
Array [
Object {
Expand Down Expand Up @@ -211,6 +213,7 @@ exports[`CreateCoursePage renders page correctly with course create in progress
isCreating={true}
keepDirtyOnReinitialize={false}
onSubmit={[Function]}
onSubmitFail={[Function]}
organizations={
Array [
Object {
Expand Down Expand Up @@ -277,6 +280,7 @@ exports[`CreateCoursePage renders page correctly with course create success 1`]
isCreating={false}
keepDirtyOnReinitialize={false}
onSubmit={[Function]}
onSubmitFail={[Function]}
organizations={
Array [
Object {
Expand Down Expand Up @@ -384,6 +388,7 @@ exports[`CreateCoursePage renders page correctly with org error 1`] = `
isCreating={false}
keepDirtyOnReinitialize={false}
onSubmit={[Function]}
onSubmitFail={[Function]}
organizations={
Array [
Object {
Expand Down Expand Up @@ -475,6 +480,7 @@ exports[`CreateCoursePage renders page correctly with organizations 1`] = `
isCreating={false}
keepDirtyOnReinitialize={false}
onSubmit={[Function]}
onSubmitFail={[Function]}
organizations={
Array [
Object {
Expand Down
9 changes: 7 additions & 2 deletions src/components/CreateCoursePage/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class CreateCoursePage extends React.Component {
const priceData = formatPriceData(options, this.props.courseOptions);
const courseData = {
...priceData,
org: options.org,
org: options.org.value,
product_source: options.source || DEFAULT_PRODUCT_SOURCE,
title: options.title,
number: options.number,
Expand Down Expand Up @@ -122,7 +122,12 @@ class CreateCoursePage extends React.Component {
} = this.state;

const organizations = publisherUserInfo.organizations ? publisherUserInfo.organizations : [];
if (organizations.length === 1) { initialValues.org = organizations[0].key; }
if (organizations.length === 1) {
initialValues.org = {
label: organizations[0].name,
value: organizations[0].key,
};
}

const sources = productSourceOptions.productSources ? productSourceOptions.productSources : [];
if (sources.length === 1) { initialValues.source = sources[0].slug; }
Expand Down
86 changes: 54 additions & 32 deletions src/components/ReduxFormCreatableSelect/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import Select from 'react-select';
import AsyncCreatableSelect from 'react-select/async-creatable';
import CreatableSelect from 'react-select/creatable';

Expand All @@ -13,66 +14,87 @@ const ReduxFormCreatableSelect = props => {
meta: {
touched, error,
},
defaultOptions, loadOptions, label, isMulti, isAsync,
defaultOptions, loadOptions, label, isCreatable, isMulti, isAsync,
createOptionValidator, disabled, isSearchable, options, formatCreateLabel,
placeholder,
} = props;

const selectProps = {
const creatableOnlyProps = {
isMulti,
isValidNewOption: createOptionValidator,
formatCreateLabel,
};

const commonProps = {
isSearchable,
value,
isDisabled: disabled,
placeholder,
formatCreateLabel,
onChange: val => onChange(val),
onBlur: () => onBlur(value),

};

const FormCreatableSelect = isAsync
? (
<AsyncCreatableSelect
className="select-container"
classNamePrefix="react-select-async"
defaultOptions={defaultOptions}
isValidNewOption={createOptionValidator}
cacheOptions
loadOptions={loadOptions}
{...commonProps}
{...creatableOnlyProps}
/>
)
: (
<CreatableSelect
className="select-container"
classNamePrefix={`${touched && error ? 'danger' : ''} react-select`}
placeholder={placeholder}
components={{
IndicatorSeparator: () => null,
}}
options={options}
{...commonProps}
{...creatableOnlyProps}
/>
);

const FormSimpleSelect = (
<Select
className="select-container"
classNamePrefix={`${touched && error ? 'danger' : ''} react-select`}
placeholder={placeholder}
components={{
IndicatorSeparator: () => null,
}}
options={options}
{...commonProps}
/>
);

return (
<div className="mb-3" name={name}>
<div className="mb-3 mr-2" name={name}>
<div className="mb-2.5">
{label}
</div>
{touched && error
&& (
<Alert variant="danger" className="mb-2.5">{error}</Alert>
)}
{isAsync
? (
<AsyncCreatableSelect
className="select-container"
classNamePrefix="react-select-async"
defaultOptions={defaultOptions}
isValidNewOption={createOptionValidator}
cacheOptions
loadOptions={loadOptions}
{...selectProps}
/>
)
: (
<CreatableSelect
className="select-container"
classNamePrefix={`${touched && error ? 'danger' : ''} react-select`}
placeholder={placeholder}
components={{
IndicatorSeparator: () => null,
}}
options={options}
onChange={val => onChange(val)}
onBlur={() => onBlur(value)}
{...selectProps}
/>
)}
{/* if isCreatable prop is set, render a creatable select else render a simple one */}
{isCreatable
? FormCreatableSelect
: FormSimpleSelect}
</div>
);
};

ReduxFormCreatableSelect.defaultProps = {
disabled: false,
loadOptions: () => null,
isCreatable: false,
isMulti: false,
isAsync: false,
isSearchable: true,
Expand Down Expand Up @@ -106,11 +128,11 @@ ReduxFormCreatableSelect.propTypes = {
label: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
})).isRequired,

formatCreateLabel: PropTypes.func,
createOptionValidator: PropTypes.func,
disabled: PropTypes.bool,
loadOptions: PropTypes.func,
isCreatable: PropTypes.bool,
isMulti: PropTypes.bool,
isAsync: PropTypes.bool,
isSearchable: PropTypes.bool,
Expand Down
Loading

0 comments on commit 371d7de

Please sign in to comment.