Skip to content

Commit

Permalink
feat(core/spinner): Consolidate spinners (#9255)
Browse files Browse the repository at this point in the history
* feat(core/spinner): Consolidate spinners

chore(core): Move loadingIndicator from managed to widgets

* chore(spinner): Angular compatibility

chore(spinner): Angular compatibility
  • Loading branch information
caseyhebebrand committed Jun 1, 2021
1 parent 8a1c855 commit e5375f0
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const submitButtonComponent: IComponentOptions = {
template: `
<button class="btn btn-primary" ng-disabled="$ctrl.isDisabled" ng-click="$ctrl.onClick()">
<i ng-if="!$ctrl.submitting" class="far fa-check-circle"></i>
<button-busy-indicator ng-if="$ctrl.submitting"></button-busy-indicator>
<loading-spinner ng-if="$ctrl.submitting" mode="'circular'"></loading-spinner>
{{$ctrl.label || ($ctrl.isNew ? 'Create' : 'Update')}}
</button>`,
};
Expand Down
8 changes: 3 additions & 5 deletions app/scripts/modules/core/src/reactShims/ngReact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,11 @@ import { ITaskMonitorProps } from 'core/task/monitor/TaskMonitorWrapper';
import { TaskMonitorWrapper } from 'core/task/monitor/TaskMonitorWrapper';
import { IAccountRegionClusterSelectorProps } from 'core/widgets/AccountRegionClusterSelector';
import { accountRegionClusterSelectorWrapperComponent } from 'core/widgets/accountRegionClusterSelectorWrapper.component';
import { ButtonBusyIndicator as ButtonBusyIndicatorComponent } from 'core/widgets/spinners/Spinner';

import { IButtonBusyIndicatorProps } from '../forms/buttonBusyIndicator/ButtonBusyIndicator';
import { buttonBusyIndicatorComponent } from '../forms/buttonBusyIndicator/buttonBusyIndicator.component';
import { IHelpFieldProps } from '../help/HelpField';
import { helpFieldWrapperComponent } from '../help/helpField.component';
import { ReactInject } from './react.injector';
import { ILegacySpinnerProps, spinnerWrapperComponent } from '../widgets/Spinner';

import IInjectorService = angular.auto.IInjectorService;

Expand All @@ -41,12 +39,12 @@ export class NgReactInjector extends ReactInject {

// Reactified components
public AccountRegionClusterSelector: React.ComponentClass<IAccountRegionClusterSelectorProps> = angular2react('accountRegionClusterSelectorWrapper', accountRegionClusterSelectorWrapperComponent, this.$injectorProxy) as any;
public ButtonBusyIndicator: React.ComponentClass<IButtonBusyIndicatorProps> = angular2react('buttonBusyIndicator', buttonBusyIndicatorComponent, this.$injectorProxy) as any;
public ButtonBusyIndicator: React.FunctionComponent<{}> = ButtonBusyIndicatorComponent;
public EntitySource: React.ComponentClass<IEntitySourceProps> = angular2react('entitySource', entitySourceComponent, this.$injectorProxy) as any;
public HelpField: React.ComponentClass<IHelpFieldProps> = angular2react('helpFieldWrapper', helpFieldWrapperComponent, this.$injectorProxy) as any;
public InstanceArchetypeSelector: React.ComponentClass<IInstanceArchetypeSelectorProps> = angular2react('v2InstanceArchetypeSelector', v2InstanceArchetypeSelector, this.$injectorProxy) as any;
public InstanceTypeSelector: React.ComponentClass<IInstanceTypeSelectorProps> = angular2react('v2InstanceTypeSelector', v2InstanceTypeSelector, this.$injectorProxy);
public LegacySpinner: React.ComponentClass<ILegacySpinnerProps> = angular2react('spinnerWrapper', spinnerWrapperComponent, this.$injectorProxy) as any;
public LegacySpinner: React.FunctionComponent<{}> = ButtonBusyIndicatorComponent;
public NumberList: React.ComponentClass<INumberListProps> = angular2react('numberListWrapper', numberListWrapperComponent, this.$injectorProxy) as any;
public StageSummaryWrapper: React.ComponentClass<IStageSummaryWrapperProps> = angular2react('stageSummary', stageSummaryComponent, this.$injectorProxy) as any;
public StepExecutionDetailsWrapper: React.ComponentClass<IStepExecutionDetailsWrapperProps> = angular2react('stepExecutionDetails', stepExecutionDetailsComponent, this.$injectorProxy) as any;
Expand Down
72 changes: 45 additions & 27 deletions app/scripts/modules/core/src/widgets/spinners/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
import classnames from 'classnames';
import React from 'react';

import { ReactComponent as LoadingIndicator } from './loadingIndicator.svg';

export interface ISpinnerProps {
size?: 'nano' | 'small' | 'medium' | 'large';
color?: string;
message?: string;
mode?: 'circular' | 'horizontal';
fullWidth?: boolean;
size?: 'nano' | 'small' | 'medium' | 'large';
}

export class Spinner extends React.Component<ISpinnerProps> {
public getBarRows(): React.ReactNode[] {
const { size } = this.props;
export const Spinner = ({
color = '#ffffff',
fullWidth,
message,
mode = 'horizontal',
size = 'small',
}: ISpinnerProps) => {
if (mode === 'circular') {
const sizeToHeight = {
nano: 12,
small: 16,
medium: 24,
large: 32,
};

return <LoadingIndicator style={{ height: sizeToHeight[size], fill: color }} />;
}

const getBarRows = (): React.ReactNode[] => {
let count = 3;

if (size) {
Expand All @@ -26,27 +46,25 @@ export class Spinner extends React.Component<ISpinnerProps> {
rows.push(<div key={i} className="bar" />);
}
return rows;
}
};

public render(): React.ReactElement<Spinner> {
const { size, message, fullWidth } = this.props;
const messageClassNames = `message color-text-accent ${size === 'medium' ? 'heading-4' : 'heading-2'}`;

const messageNode = ['medium', 'large'].includes(size) && (
<div className={messageClassNames}>{message || 'Loading ...'}</div>
);

const bars = ['medium', 'large'].includes(size) ? (
<div className="bars">{this.getBarRows()}</div>
) : (
this.getBarRows()
);

return (
<div className={classnames('load', size || 'small', { 'full-width': fullWidth })}>
{messageNode}
{bars}
</div>
);
}
}
const messageClassNames = `message color-text-accent ${size === 'medium' ? 'heading-4' : 'heading-2'}`;
const messageNode = ['medium', 'large'].includes(size) && (
<div className={messageClassNames}>{message || 'Loading ...'}</div>
);

const bars = ['medium', 'large'].includes(size) ? <div className="bars">{getBarRows()}</div> : getBarRows();

return (
<div className={classnames('load', size || 'small', { 'full-width': fullWidth })}>
{messageNode}
{bars}
</div>
);
};

/**
* Note: This is a temporary component for backwards compatibility in NgReact.
* Once core is bumped, the `ButtonBusyIndicator` and `LegacySpinner` from NgReact can be replaced with `Spinner` in the other modules and removed.
*/
export const ButtonBusyIndicator = () => <Spinner mode="circular" />;
76 changes: 76 additions & 0 deletions app/scripts/modules/core/src/widgets/spinners/loadingIndicator.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ export const SPINNER_COMPONENT = 'spinnaker.core.spinner.component';

module(SPINNER_COMPONENT, []).component(
'loadingSpinner',
react2angular(withErrorBoundary(Spinner, 'loadingSpinner'), ['size', 'message']),
react2angular(withErrorBoundary(Spinner, 'loadingSpinner'), ['size', 'message', 'mode']),
);

0 comments on commit e5375f0

Please sign in to comment.