* feat: Implement edX forums advanced setting editor This change implements the UX for the advanced settings editor for the internal edX forums. * Stepper now intelligently shows/hides drop shadows on the header and footer Also organized the component into a more Paragon-like organization with the sub-components hanging off the main one. * Organizing full screen modal in a more Paragon-like way. Also fixing a few minor styling issues. * Massaging SettingsEditor into LegacyConfigForm - Reorganizing into apps/legacy directory from settings/base-forum - Using Formik and Yup for managing form data - Refactoring FormSwitchGroup a little and adding data-related properties - Also moving FormSwitchGroup into ‘generic’ - Some initial attempts at error validation in LegacyConfigForm (a blackout dates regex which doesn’t seem to work yet) - Sub-sections of config now fold and animate when their parent is toggled. * Minor naming and refactoring of Discussions component - Event handlers should be named handle*, not on*. Oops. Got over-zealous. - Organizing paths into some variables at the top of the component. The pages and resources path should probably be passed in. * Hooking up and organizing LTI and Legacy forms - The LTI form moves into app/lti - Adds in rendering of the legacy discussions form. - Splits up the messages file a bit (app/lti/messages.js exists now) - Removing unnecessary h1 in the LtiConfigForm. * Removing ‘info’ blue coloring from the pages & resources view. Co-authored-by: Kshitij Sobti <kshitij@sobti.in>
70 lines
1.9 KiB
JavaScript
70 lines
1.9 KiB
JavaScript
import React, { useContext } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import classNames from 'classnames';
|
|
import Icon from './Icon';
|
|
import Line from './Line';
|
|
import { StepperContext } from './StepperContext';
|
|
|
|
export default function Header({ steps, className }) {
|
|
const { isAtTop } = useContext(StepperContext);
|
|
|
|
return (
|
|
<div
|
|
className={classNames(
|
|
'p-2',
|
|
'd-flex',
|
|
'border-bottom',
|
|
'border-light',
|
|
'justify-content-center',
|
|
'align-items-center',
|
|
className,
|
|
)}
|
|
style={{
|
|
// zIndex raises this div to a higher 'layer' so that its drop shadow falls
|
|
// above the content in the Body component. Without this, any backgrounds in the Body
|
|
// cut off the drop shadow as if they're 'higher' than it.
|
|
zIndex: '1',
|
|
boxShadow: isAtTop ? null : '0 0.25rem 0.5rem rgba(0, 0, 0, 0.3)',
|
|
}}
|
|
>
|
|
{steps.map(({ iconLabel, label, incomplete }, index) => {
|
|
const isNotLastStep = index < steps.length - 1;
|
|
return (
|
|
// eslint-disable-next-line react/no-array-index-key
|
|
<React.Fragment key={`${index}-${label}`}>
|
|
<Icon className={incomplete ? 'bg-light-900' : 'bg-primary'}>
|
|
{iconLabel || index + 1}
|
|
</Icon>
|
|
<span className={classNames(
|
|
'font-weight-bold',
|
|
{ 'text-light-900': incomplete },
|
|
)}
|
|
>{label}
|
|
</span>
|
|
{isNotLastStep && (
|
|
<Line />
|
|
)}
|
|
</React.Fragment>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
Header.propTypes = {
|
|
steps: PropTypes.arrayOf(
|
|
PropTypes.shape({
|
|
iconLabel: PropTypes.oneOfType([
|
|
PropTypes.string,
|
|
PropTypes.node,
|
|
]),
|
|
label: PropTypes.string.isRequired,
|
|
}),
|
|
).isRequired,
|
|
className: PropTypes.string,
|
|
};
|
|
|
|
Header.defaultProps = {
|
|
className: null,
|
|
};
|